blob: 990426f36040ed0788876a51a8378e60500c8ed0 [file] [log] [blame]
Vernon Mauery9e801a22018-10-12 13:20:49 -07001#include "sd_event_loop.hpp"
2
3#include "main.hpp"
4#include "message_handler.hpp"
5
Dave Cobbley2c15f0c2017-11-13 16:19:09 -08006#include <netinet/in.h>
Tom Joseph7fd26dd2017-03-14 15:26:26 +05307#include <sys/ioctl.h>
Dave Cobbley2c15f0c2017-11-13 16:19:09 -08008#include <sys/socket.h>
Tom Joseph7fd26dd2017-03-14 15:26:26 +05309#include <systemd/sd-daemon.h>
Vernon Mauery9e801a22018-10-12 13:20:49 -070010
Vernon Mauerycbccb052018-10-24 13:52:22 -070011#include <boost/asio/io_context.hpp>
Tom Joseph7fd26dd2017-03-14 15:26:26 +053012#include <phosphor-logging/log.hpp>
Vernon Mauerycbccb052018-10-24 13:52:22 -070013#include <sdbusplus/asio/sd_event.hpp>
Tom Joseph7fd26dd2017-03-14 15:26:26 +053014
15namespace eventloop
16{
17using namespace phosphor::logging;
18
Vernon Mauery7a0142c2018-11-09 08:38:16 -080019void EventLoop::handleRmcpPacket()
Tom Joseph7fd26dd2017-03-14 15:26:26 +053020{
Tom Joseph7fd26dd2017-03-14 15:26:26 +053021 try
22 {
Vernon Mauery7a0142c2018-11-09 08:38:16 -080023 auto channelPtr = std::make_shared<udpsocket::Channel>(udpSocket);
Tom Joseph7fd26dd2017-03-14 15:26:26 +053024
25 // Initialize the Message Handler with the socket channel
Vernon Mauery7a0142c2018-11-09 08:38:16 -080026 auto msgHandler = std::make_shared<message::Handler>(channelPtr);
Tom Joseph7fd26dd2017-03-14 15:26:26 +053027
Tom Joseph7fd26dd2017-03-14 15:26:26 +053028 // Read the incoming IPMI packet
Vernon Mauery7a0142c2018-11-09 08:38:16 -080029 std::shared_ptr<message::Message> inMessage(msgHandler->receive());
Tom Joseph7fd26dd2017-03-14 15:26:26 +053030 if (inMessage == nullptr)
31 {
Vernon Mauery7a0142c2018-11-09 08:38:16 -080032 return;
Tom Joseph7fd26dd2017-03-14 15:26:26 +053033 }
34
35 // Execute the Command
Vernon Mauery7a0142c2018-11-09 08:38:16 -080036 std::shared_ptr<message::Message> outMessage =
37 msgHandler->executeCommand(inMessage);
Tom Joseph7fd26dd2017-03-14 15:26:26 +053038 if (outMessage == nullptr)
39 {
Vernon Mauery7a0142c2018-11-09 08:38:16 -080040 return;
Tom Joseph7fd26dd2017-03-14 15:26:26 +053041 }
Tom Joseph7fd26dd2017-03-14 15:26:26 +053042 // Send the response IPMI Message
Vernon Mauery7a0142c2018-11-09 08:38:16 -080043 msgHandler->send(outMessage);
Tom Joseph7fd26dd2017-03-14 15:26:26 +053044 }
Vernon Mauery7a0142c2018-11-09 08:38:16 -080045 catch (const std::exception& e)
Tom Joseph7fd26dd2017-03-14 15:26:26 +053046 {
Vernon Mauery7a0142c2018-11-09 08:38:16 -080047 log<level::ERR>("Executing the IPMI message failed",
48 entry("EXCEPTION=%s", e.what()));
Tom Joseph7fd26dd2017-03-14 15:26:26 +053049 }
Vernon Mauery7a0142c2018-11-09 08:38:16 -080050}
Tom Joseph7fd26dd2017-03-14 15:26:26 +053051
Vernon Mauery7a0142c2018-11-09 08:38:16 -080052void EventLoop::startRmcpReceive()
53{
54 udpSocket->async_wait(boost::asio::socket_base::wait_read,
55 [this](const boost::system::error_code& ec) {
56 if (!ec)
57 {
58 io->post([this]() { startRmcpReceive(); });
59 handleRmcpPacket();
60 }
61 });
Tom Joseph7fd26dd2017-03-14 15:26:26 +053062}
63
Tom Joseph56f24e92017-03-14 16:06:31 +053064static int consoleInputHandler(sd_event_source* es, int fd, uint32_t revents,
65 void* userdata)
66{
67 try
68 {
69 int readSize = 0;
70
71 if (ioctl(fd, FIONREAD, &readSize) < 0)
72 {
73 log<level::ERR>("ioctl failed for FIONREAD:",
Vernon Mauery9e801a22018-10-12 13:20:49 -070074 entry("ERRNO=%d", errno));
Tom Joseph56f24e92017-03-14 16:06:31 +053075 return 0;
76 }
77
78 std::vector<uint8_t> buffer(readSize);
79 auto bufferSize = buffer.size();
80 ssize_t readDataLen = 0;
81
82 readDataLen = read(fd, buffer.data(), bufferSize);
83
84 // Update the Console buffer with data read from the socket
85 if (readDataLen > 0)
86 {
87 buffer.resize(readDataLen);
88 std::get<sol::Manager&>(singletonPool).dataBuffer.write(buffer);
89 }
90 else if (readDataLen == 0)
91 {
92 log<level::ERR>("Connection Closed for host console socket");
93 }
94 else if (readDataLen < 0) // Error
95 {
96 log<level::ERR>("Reading from host console socket failed:",
Vernon Mauery9e801a22018-10-12 13:20:49 -070097 entry("ERRNO=%d", errno));
Tom Joseph56f24e92017-03-14 16:06:31 +053098 }
99 }
100 catch (std::exception& e)
101 {
102 log<level::ERR>(e.what());
103 }
104
105 return 0;
106}
107
Tom Josephe989c362017-03-14 17:16:50 +0530108static int charAccTimerHandler(sd_event_source* s, uint64_t usec,
Vernon Mauery9e801a22018-10-12 13:20:49 -0700109 void* userdata)
Tom Josephe989c362017-03-14 17:16:50 +0530110{
Tom Josephe989c362017-03-14 17:16:50 +0530111 auto bufferSize = std::get<sol::Manager&>(singletonPool).dataBuffer.size();
112
113 try
114 {
Patrick Venturea65e30d2018-10-26 17:55:08 -0700115 // The instance is hardcoded to 1, in the case of supporting multiple
116 // payload instances we would need to populate it from userdata
117 uint8_t instance = 1;
118
Vernon Mauery9e801a22018-10-12 13:20:49 -0700119 if (bufferSize > 0)
Tom Josephe989c362017-03-14 17:16:50 +0530120 {
Vernon Mauery9e801a22018-10-12 13:20:49 -0700121 auto& context =
122 std::get<sol::Manager&>(singletonPool).getContext(instance);
Tom Joseph73063142017-03-14 18:20:20 +0530123
Patrick Venturea65e30d2018-10-26 17:55:08 -0700124 int rc = context.sendOutboundPayload();
Tom Joseph73063142017-03-14 18:20:20 +0530125
126 if (rc == 0)
127 {
128 return 0;
129 }
Tom Josephe989c362017-03-14 17:16:50 +0530130 }
131
Vernon Mauery9e801a22018-10-12 13:20:49 -0700132 std::get<eventloop::EventLoop&>(singletonPool)
133 .switchTimer(instance, Timers::ACCUMULATE, true);
Tom Josephe989c362017-03-14 17:16:50 +0530134 }
135 catch (std::exception& e)
136 {
137 log<level::ERR>(e.what());
138 }
139
140 return 0;
141}
142
Vernon Mauery9e801a22018-10-12 13:20:49 -0700143static int retryTimerHandler(sd_event_source* s, uint64_t usec, void* userdata)
Tom Josephe989c362017-03-14 17:16:50 +0530144{
Tom Josephe989c362017-03-14 17:16:50 +0530145 try
146 {
Patrick Venturea65e30d2018-10-26 17:55:08 -0700147 // The instance is hardcoded to 1, in the case of supporting multiple
148 // payload instances we would need to populate it from userdata
149 uint8_t instance = 1;
150
Vernon Mauery9e801a22018-10-12 13:20:49 -0700151 auto& context =
152 std::get<sol::Manager&>(singletonPool).getContext(instance);
Tom Josephe989c362017-03-14 17:16:50 +0530153
154 if (context.retryCounter)
155 {
156 --context.retryCounter;
Vernon Mauery9e801a22018-10-12 13:20:49 -0700157 std::get<eventloop::EventLoop&>(singletonPool)
158 .switchTimer(instance, Timers::RETRY, true);
Tom Joseph2fd466f2017-03-30 08:16:47 +0530159 context.resendPayload(sol::Context::noClear);
Tom Josephe989c362017-03-14 17:16:50 +0530160 }
161 else
162 {
163 context.retryCounter = context.maxRetryCount;
Tom Joseph2fd466f2017-03-30 08:16:47 +0530164 context.resendPayload(sol::Context::clear);
Vernon Mauery9e801a22018-10-12 13:20:49 -0700165 std::get<eventloop::EventLoop&>(singletonPool)
166 .switchTimer(instance, Timers::RETRY, false);
167 std::get<eventloop::EventLoop&>(singletonPool)
168 .switchTimer(instance, Timers::ACCUMULATE, true);
Tom Josephe989c362017-03-14 17:16:50 +0530169 }
170 }
171 catch (std::exception& e)
172 {
173 log<level::ERR>(e.what());
174 }
175
176 return 0;
177}
178
Vernon Mauerycbccb052018-10-24 13:52:22 -0700179int EventLoop::startEventLoop()
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530180{
Vernon Mauerycbccb052018-10-24 13:52:22 -0700181 sdbusplus::asio::sd_event_wrapper sdEvents(*io);
182 event = sdEvents.get();
Marri Devender Rao3ecf0a12017-07-13 08:07:22 -0500183
Vernon Mauery22c8a212018-10-24 14:51:23 -0700184 // set up boost::asio signal handling
185 boost::asio::signal_set signals(*io, SIGINT, SIGTERM);
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800186 signals.async_wait(
187 [this](const boost::system::error_code& error, int signalNumber) {
188 udpSocket->cancel();
189 udpSocket->close();
190 io->stop();
191 });
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530192
Vernon Mauery9e801a22018-10-12 13:20:49 -0700193 // Create our own socket if SysD did not supply one.
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800194 int listensFdCount = sd_listen_fds(0);
195 if (listensFdCount == 1)
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530196 {
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800197 if (sd_is_socket(SD_LISTEN_FDS_START, AF_UNSPEC, SOCK_DGRAM, -1))
198 {
199 udpSocket = std::make_shared<boost::asio::ip::udp::socket>(
200 *io, boost::asio::ip::udp::v6(), SD_LISTEN_FDS_START);
201 }
Dave Cobbley2c15f0c2017-11-13 16:19:09 -0800202 }
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800203 else if (listensFdCount > 1)
Dave Cobbley2c15f0c2017-11-13 16:19:09 -0800204 {
205 log<level::ERR>("Too many file descriptors received");
Vernon Mauerycbccb052018-10-24 13:52:22 -0700206 return EXIT_FAILURE;
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530207 }
Vernon Mauery7a0142c2018-11-09 08:38:16 -0800208 if (!udpSocket)
209 {
210 udpSocket = std::make_shared<boost::asio::ip::udp::socket>(
211 *io, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v6(),
212 IPMI_STD_PORT));
213 if (!udpSocket)
214 {
215 log<level::ERR>("Failed to start listening on RMCP socket");
216 return EXIT_FAILURE;
217 }
218 }
219 startRmcpReceive();
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530220
Vernon Mauerycbccb052018-10-24 13:52:22 -0700221 io->run();
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530222
Vernon Mauerycbccb052018-10-24 13:52:22 -0700223 return EXIT_SUCCESS;
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530224}
225
Tom Joseph56f24e92017-03-14 16:06:31 +0530226void EventLoop::startHostConsole(const sol::CustomFD& fd)
227{
228 int rc = 0;
229
Vernon Mauery9e801a22018-10-12 13:20:49 -0700230 if ((fd() == -1) || hostConsole.get())
Tom Joseph56f24e92017-03-14 16:06:31 +0530231 {
232 throw std::runtime_error("Console descriptor already added");
233 }
234
235 sd_event_source* source = nullptr;
236
237 // Add the fd to the event loop for EPOLLIN
Vernon Mauery9e801a22018-10-12 13:20:49 -0700238 rc = sd_event_add_io(event, &source, fd(), EPOLLIN, consoleInputHandler,
239 nullptr);
Tom Joseph56f24e92017-03-14 16:06:31 +0530240 if (rc < 0)
241 {
242 throw std::runtime_error("Failed to add socket descriptor");
243 }
244
245 hostConsole.reset(source);
Vernon Mauery9e801a22018-10-12 13:20:49 -0700246 source = nullptr;
Tom Joseph56f24e92017-03-14 16:06:31 +0530247}
248
Tom Josephcfbd43f2017-03-14 16:14:03 +0530249void EventLoop::stopHostConsole()
250{
Tom Josephcfbd43f2017-03-14 16:14:03 +0530251 if (hostConsole.get())
252 {
253 // Disable the host console payload
Patrick Venturea65e30d2018-10-26 17:55:08 -0700254 int rc = sd_event_source_set_enabled(hostConsole.get(), SD_EVENT_OFF);
Tom Josephcfbd43f2017-03-14 16:14:03 +0530255 if (rc < 0)
256 {
257 log<level::ERR>("Failed to disable the host console socket",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700258 entry("RC=%d", rc));
Tom Josephcfbd43f2017-03-14 16:14:03 +0530259 }
260
261 hostConsole.reset();
262 }
263}
264
Tom Josephe989c362017-03-14 17:16:50 +0530265void EventLoop::startSOLPayloadInstance(uint8_t payloadInst,
266 IntervalType accumulateInterval,
267 IntervalType retryInterval)
268{
269 auto instance = payloadInst;
270 sd_event_source* accTimerSource = nullptr;
271 sd_event_source* retryTimerSource = nullptr;
272 int rc = 0;
273 uint64_t currentTime = 0;
274
275 rc = sd_event_now(event, CLOCK_MONOTONIC, &currentTime);
276 if (rc < 0)
277 {
278 log<level::ERR>("Failed to get the current timestamp",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700279 entry("RC=%d", rc));
Tom Josephe989c362017-03-14 17:16:50 +0530280 throw std::runtime_error("Failed to get current timestamp");
281 }
282
283 // Create character accumulate timer
Vernon Mauery9e801a22018-10-12 13:20:49 -0700284 rc = sd_event_add_time(event, &accTimerSource, CLOCK_MONOTONIC,
285 currentTime + accumulateInterval.count(), 0,
286 charAccTimerHandler, static_cast<void*>(&instance));
Tom Josephe989c362017-03-14 17:16:50 +0530287 if (rc < 0)
288 {
289 log<level::ERR>("Failed to setup the accumulate timer",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700290 entry("RC=%d", rc));
Tom Josephe989c362017-03-14 17:16:50 +0530291 throw std::runtime_error("Failed to setup accumulate timer");
292 }
293
294 // Create retry interval timer and add to the event loop
Vernon Mauery9e801a22018-10-12 13:20:49 -0700295 rc = sd_event_add_time(event, &retryTimerSource, CLOCK_MONOTONIC,
296 currentTime + retryInterval.count(), 0,
297 retryTimerHandler, static_cast<void*>(&instance));
Tom Josephe989c362017-03-14 17:16:50 +0530298 if (rc < 0)
299 {
Vernon Mauery9e801a22018-10-12 13:20:49 -0700300 log<level::ERR>("Failed to setup the retry timer", entry("RC=%d", rc));
Tom Josephe989c362017-03-14 17:16:50 +0530301 throw std::runtime_error("Failed to setup retry timer");
302 }
303
304 // Enable the Character Accumulate Timer
305 rc = sd_event_source_set_enabled(accTimerSource, SD_EVENT_ONESHOT);
306 if (rc < 0)
307 {
308 log<level::ERR>("Failed to enable the accumulate timer",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700309 entry("RC=%d", rc));
Tom Josephe989c362017-03-14 17:16:50 +0530310 throw std::runtime_error("Failed to enable accumulate timer");
311 }
312
313 // Disable the Retry Interval Timer
314 rc = sd_event_source_set_enabled(retryTimerSource, SD_EVENT_OFF);
315 if (rc < 0)
316 {
317 log<level::ERR>("Failed to disable the retry timer",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700318 entry("RC=%d", rc));
Tom Josephe989c362017-03-14 17:16:50 +0530319 throw std::runtime_error("Failed to disable retry timer");
320 }
321
322 EventSource accEventSource(accTimerSource);
323 EventSource retryEventSource(retryTimerSource);
324 accTimerSource = nullptr;
325 retryTimerSource = nullptr;
326
327 TimerMap timer;
328 timer.emplace(Timers::ACCUMULATE, std::make_tuple(std::move(accEventSource),
329 accumulateInterval));
Vernon Mauery9e801a22018-10-12 13:20:49 -0700330 timer.emplace(Timers::RETRY,
331 std::make_tuple(std::move(retryEventSource), retryInterval));
Tom Josephe989c362017-03-14 17:16:50 +0530332 payloadInfo.emplace(instance, std::move(timer));
333}
334
Tom Josepha891eb72017-03-14 17:25:01 +0530335void EventLoop::stopSOLPayloadInstance(uint8_t payloadInst)
336{
337 auto iter = payloadInfo.find(payloadInst);
338 if (iter == payloadInfo.end())
339 {
340 log<level::ERR>("SOL Payload instance not found",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700341 entry("PAYLOADINST=%d", payloadInst));
Tom Josepha891eb72017-03-14 17:25:01 +0530342 throw std::runtime_error("SOL payload instance not found");
343 }
344
345 int rc = 0;
346
347 /* Destroy the character accumulate timer event source */
348 rc = sd_event_source_set_enabled(
Vernon Mauery9e801a22018-10-12 13:20:49 -0700349 (std::get<0>(iter->second.at(Timers::ACCUMULATE))).get(), SD_EVENT_OFF);
Tom Josepha891eb72017-03-14 17:25:01 +0530350 if (rc < 0)
351 {
352 log<level::ERR>("Failed to disable the character accumulate timer",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700353 entry("RC=%d", rc));
Tom Josepha891eb72017-03-14 17:25:01 +0530354 payloadInfo.erase(payloadInst);
355 throw std::runtime_error("Failed to disable accumulate timer");
356 }
357
358 /* Destroy the retry interval timer event source */
359 rc = sd_event_source_set_enabled(
Vernon Mauery9e801a22018-10-12 13:20:49 -0700360 (std::get<0>(iter->second.at(Timers::RETRY))).get(), SD_EVENT_OFF);
Tom Josepha891eb72017-03-14 17:25:01 +0530361 if (rc < 0)
362 {
363 log<level::ERR>("Failed to disable the retry timer",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700364 entry("RC=%d", rc));
Tom Josepha891eb72017-03-14 17:25:01 +0530365 payloadInfo.erase(payloadInst);
366 throw std::runtime_error("Failed to disable retry timer");
367 }
368
369 payloadInfo.erase(payloadInst);
370}
371
Vernon Mauery9e801a22018-10-12 13:20:49 -0700372void EventLoop::switchTimer(uint8_t payloadInst, Timers type, bool status)
Tom Joseph0a0d8f62017-03-14 17:04:58 +0530373{
374 auto iter = payloadInfo.find(payloadInst);
375 if (iter == payloadInfo.end())
376 {
377 log<level::ERR>("SOL Payload instance not found",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700378 entry("PAYLOADINST=%d", payloadInst));
Tom Joseph0a0d8f62017-03-14 17:04:58 +0530379 throw std::runtime_error("SOL Payload instance not found");
380 }
381
382 int rc = 0;
383 auto source = (std::get<0>(iter->second.at(type))).get();
384 auto interval = std::get<1>(iter->second.at(type));
385
386 // Turn OFF the timer
387 if (!status)
388 {
389 rc = sd_event_source_set_enabled(source, SD_EVENT_OFF);
390 if (rc < 0)
391 {
392 log<level::ERR>("Failed to disable the timer", entry("RC=%d", rc));
393 throw std::runtime_error("Failed to disable timer");
394 }
395 return;
396 }
397
398 // Turn ON the timer
399 uint64_t currentTime = 0;
400 rc = sd_event_now(event, CLOCK_MONOTONIC, &currentTime);
401 if (rc < 0)
402 {
403 log<level::ERR>("Failed to get the current timestamp",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700404 entry("RC=%d", rc));
Tom Joseph0a0d8f62017-03-14 17:04:58 +0530405 throw std::runtime_error("Failed to get current timestamp");
406 }
407
408 rc = sd_event_source_set_time(source, currentTime + interval.count());
409 if (rc < 0)
410 {
411 log<level::ERR>("sd_event_source_set_time function failed",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700412 entry("RC=%d", rc));
Tom Joseph0a0d8f62017-03-14 17:04:58 +0530413 throw std::runtime_error("sd_event_source_set_time function failed");
414 }
415
416 rc = sd_event_source_set_enabled(source, SD_EVENT_ONESHOT);
417 if (rc < 0)
418 {
Vernon Mauery9e801a22018-10-12 13:20:49 -0700419 log<level::ERR>("Failed to enable the timer", entry("RC=%d", rc));
Tom Joseph0a0d8f62017-03-14 17:04:58 +0530420 throw std::runtime_error("Failed to enable timer");
421 }
422}
423
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530424} // namespace eventloop