blob: 3e42d6c862510c93c0a9a18a6b68b89d9db4c0be [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
19static int udp623Handler(sd_event_source* es, int fd, uint32_t revents,
20 void* userdata)
21{
22 std::shared_ptr<udpsocket::Channel> channelPtr;
23 struct timeval timeout;
24 timeout.tv_sec = SELECT_CALL_TIMEOUT;
25 timeout.tv_usec = 0;
26
27 try
28 {
29 channelPtr.reset(new udpsocket::Channel(fd, timeout));
30
31 // Initialize the Message Handler with the socket channel
32 message::Handler msgHandler(channelPtr);
33
Tom Joseph7fd26dd2017-03-14 15:26:26 +053034 // Read the incoming IPMI packet
Vernon Maueryd999ffc2018-10-25 09:16:05 -070035 std::shared_ptr<message::Message> inMessage(msgHandler.receive());
Tom Joseph7fd26dd2017-03-14 15:26:26 +053036 if (inMessage == nullptr)
37 {
38 return 0;
39 }
40
41 // Execute the Command
Vernon Maueryd999ffc2018-10-25 09:16:05 -070042 auto outMessage = msgHandler.executeCommand(inMessage);
Tom Joseph7fd26dd2017-03-14 15:26:26 +053043 if (outMessage == nullptr)
44 {
45 return 0;
46 }
47
48 // Send the response IPMI Message
Vernon Maueryd999ffc2018-10-25 09:16:05 -070049 msgHandler.send(outMessage);
Tom Joseph7fd26dd2017-03-14 15:26:26 +053050 }
51 catch (std::exception& e)
52 {
53 log<level::ERR>("Executing the IPMI message failed");
54 log<level::ERR>(e.what());
55 }
56
57 return 0;
58}
59
Tom Joseph56f24e92017-03-14 16:06:31 +053060static int consoleInputHandler(sd_event_source* es, int fd, uint32_t revents,
61 void* userdata)
62{
63 try
64 {
65 int readSize = 0;
66
67 if (ioctl(fd, FIONREAD, &readSize) < 0)
68 {
69 log<level::ERR>("ioctl failed for FIONREAD:",
Vernon Mauery9e801a22018-10-12 13:20:49 -070070 entry("ERRNO=%d", errno));
Tom Joseph56f24e92017-03-14 16:06:31 +053071 return 0;
72 }
73
74 std::vector<uint8_t> buffer(readSize);
75 auto bufferSize = buffer.size();
76 ssize_t readDataLen = 0;
77
78 readDataLen = read(fd, buffer.data(), bufferSize);
79
80 // Update the Console buffer with data read from the socket
81 if (readDataLen > 0)
82 {
83 buffer.resize(readDataLen);
84 std::get<sol::Manager&>(singletonPool).dataBuffer.write(buffer);
85 }
86 else if (readDataLen == 0)
87 {
88 log<level::ERR>("Connection Closed for host console socket");
89 }
90 else if (readDataLen < 0) // Error
91 {
92 log<level::ERR>("Reading from host console socket failed:",
Vernon Mauery9e801a22018-10-12 13:20:49 -070093 entry("ERRNO=%d", errno));
Tom Joseph56f24e92017-03-14 16:06:31 +053094 }
95 }
96 catch (std::exception& e)
97 {
98 log<level::ERR>(e.what());
99 }
100
101 return 0;
102}
103
Tom Josephe989c362017-03-14 17:16:50 +0530104static int charAccTimerHandler(sd_event_source* s, uint64_t usec,
Vernon Mauery9e801a22018-10-12 13:20:49 -0700105 void* userdata)
Tom Josephe989c362017-03-14 17:16:50 +0530106{
Tom Josephe989c362017-03-14 17:16:50 +0530107 auto bufferSize = std::get<sol::Manager&>(singletonPool).dataBuffer.size();
108
109 try
110 {
Patrick Venturea65e30d2018-10-26 17:55:08 -0700111 // The instance is hardcoded to 1, in the case of supporting multiple
112 // payload instances we would need to populate it from userdata
113 uint8_t instance = 1;
114
Vernon Mauery9e801a22018-10-12 13:20:49 -0700115 if (bufferSize > 0)
Tom Josephe989c362017-03-14 17:16:50 +0530116 {
Vernon Mauery9e801a22018-10-12 13:20:49 -0700117 auto& context =
118 std::get<sol::Manager&>(singletonPool).getContext(instance);
Tom Joseph73063142017-03-14 18:20:20 +0530119
Patrick Venturea65e30d2018-10-26 17:55:08 -0700120 int rc = context.sendOutboundPayload();
Tom Joseph73063142017-03-14 18:20:20 +0530121
122 if (rc == 0)
123 {
124 return 0;
125 }
Tom Josephe989c362017-03-14 17:16:50 +0530126 }
127
Vernon Mauery9e801a22018-10-12 13:20:49 -0700128 std::get<eventloop::EventLoop&>(singletonPool)
129 .switchTimer(instance, Timers::ACCUMULATE, true);
Tom Josephe989c362017-03-14 17:16:50 +0530130 }
131 catch (std::exception& e)
132 {
133 log<level::ERR>(e.what());
134 }
135
136 return 0;
137}
138
Vernon Mauery9e801a22018-10-12 13:20:49 -0700139static int retryTimerHandler(sd_event_source* s, uint64_t usec, void* userdata)
Tom Josephe989c362017-03-14 17:16:50 +0530140{
Tom Josephe989c362017-03-14 17:16:50 +0530141 try
142 {
Patrick Venturea65e30d2018-10-26 17:55:08 -0700143 // The instance is hardcoded to 1, in the case of supporting multiple
144 // payload instances we would need to populate it from userdata
145 uint8_t instance = 1;
146
Vernon Mauery9e801a22018-10-12 13:20:49 -0700147 auto& context =
148 std::get<sol::Manager&>(singletonPool).getContext(instance);
Tom Josephe989c362017-03-14 17:16:50 +0530149
150 if (context.retryCounter)
151 {
152 --context.retryCounter;
Vernon Mauery9e801a22018-10-12 13:20:49 -0700153 std::get<eventloop::EventLoop&>(singletonPool)
154 .switchTimer(instance, Timers::RETRY, true);
Tom Joseph2fd466f2017-03-30 08:16:47 +0530155 context.resendPayload(sol::Context::noClear);
Tom Josephe989c362017-03-14 17:16:50 +0530156 }
157 else
158 {
159 context.retryCounter = context.maxRetryCount;
Tom Joseph2fd466f2017-03-30 08:16:47 +0530160 context.resendPayload(sol::Context::clear);
Vernon Mauery9e801a22018-10-12 13:20:49 -0700161 std::get<eventloop::EventLoop&>(singletonPool)
162 .switchTimer(instance, Timers::RETRY, false);
163 std::get<eventloop::EventLoop&>(singletonPool)
164 .switchTimer(instance, Timers::ACCUMULATE, true);
Tom Josephe989c362017-03-14 17:16:50 +0530165 }
166 }
167 catch (std::exception& e)
168 {
169 log<level::ERR>(e.what());
170 }
171
172 return 0;
173}
174
Vernon Mauerycbccb052018-10-24 13:52:22 -0700175int EventLoop::startEventLoop()
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530176{
177 int fd = -1;
178 int r = 0;
Vernon Mauery22c8a212018-10-24 14:51:23 -0700179 int listenFd;
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530180 sd_event_source* source = nullptr;
181
Vernon Mauerycbccb052018-10-24 13:52:22 -0700182 sdbusplus::asio::sd_event_wrapper sdEvents(*io);
183 event = sdEvents.get();
Marri Devender Rao3ecf0a12017-07-13 08:07:22 -0500184
Vernon Mauery22c8a212018-10-24 14:51:23 -0700185 // set up boost::asio signal handling
186 boost::asio::signal_set signals(*io, SIGINT, SIGTERM);
187 signals.async_wait([this](const boost::system::error_code& error,
188 int signalNumber) { io->stop(); });
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530189
Vernon Mauery9e801a22018-10-12 13:20:49 -0700190 // Create our own socket if SysD did not supply one.
Vernon Mauery22c8a212018-10-24 14:51:23 -0700191 listenFd = sd_listen_fds(0);
192 if (listenFd == 1)
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530193 {
Dave Cobbley2c15f0c2017-11-13 16:19:09 -0800194 fd = SD_LISTEN_FDS_START;
195 }
Vernon Mauery22c8a212018-10-24 14:51:23 -0700196 else if (listenFd > 1)
Dave Cobbley2c15f0c2017-11-13 16:19:09 -0800197 {
198 log<level::ERR>("Too many file descriptors received");
Vernon Mauerycbccb052018-10-24 13:52:22 -0700199 return 1;
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530200 }
Dave Cobbley2c15f0c2017-11-13 16:19:09 -0800201 else
202 {
203 struct sockaddr_in address;
204 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == 0)
205 {
206 r = -errno;
207 log<level::ERR>("Unable to manually open socket");
Vernon Mauerycbccb052018-10-24 13:52:22 -0700208 return EXIT_FAILURE;
Dave Cobbley2c15f0c2017-11-13 16:19:09 -0800209 }
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530210
Dave Cobbley2c15f0c2017-11-13 16:19:09 -0800211 address.sin_family = AF_INET;
212 address.sin_addr.s_addr = INADDR_ANY;
213 address.sin_port = htons(IPMI_STD_PORT);
214
Vernon Mauery9e801a22018-10-12 13:20:49 -0700215 if (bind(fd, (struct sockaddr*)&address, sizeof(address)) < 0)
Dave Cobbley2c15f0c2017-11-13 16:19:09 -0800216 {
217 r = -errno;
218 log<level::ERR>("Unable to bind socket");
Vernon Mauerycbccb052018-10-24 13:52:22 -0700219 close(fd);
220 return EXIT_FAILURE;
Dave Cobbley2c15f0c2017-11-13 16:19:09 -0800221 }
222 }
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530223
224 r = sd_event_add_io(event, &source, fd, EPOLLIN, udp623Handler, nullptr);
225 if (r < 0)
226 {
Vernon Mauerycbccb052018-10-24 13:52:22 -0700227 close(fd);
228 return EXIT_FAILURE;
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530229 }
230
231 udpIPMI.reset(source);
232 source = nullptr;
233
Vernon Mauerycbccb052018-10-24 13:52:22 -0700234 io->run();
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530235
Vernon Mauerycbccb052018-10-24 13:52:22 -0700236 return EXIT_SUCCESS;
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530237}
238
Tom Joseph56f24e92017-03-14 16:06:31 +0530239void EventLoop::startHostConsole(const sol::CustomFD& fd)
240{
241 int rc = 0;
242
Vernon Mauery9e801a22018-10-12 13:20:49 -0700243 if ((fd() == -1) || hostConsole.get())
Tom Joseph56f24e92017-03-14 16:06:31 +0530244 {
245 throw std::runtime_error("Console descriptor already added");
246 }
247
248 sd_event_source* source = nullptr;
249
250 // Add the fd to the event loop for EPOLLIN
Vernon Mauery9e801a22018-10-12 13:20:49 -0700251 rc = sd_event_add_io(event, &source, fd(), EPOLLIN, consoleInputHandler,
252 nullptr);
Tom Joseph56f24e92017-03-14 16:06:31 +0530253 if (rc < 0)
254 {
255 throw std::runtime_error("Failed to add socket descriptor");
256 }
257
258 hostConsole.reset(source);
Vernon Mauery9e801a22018-10-12 13:20:49 -0700259 source = nullptr;
Tom Joseph56f24e92017-03-14 16:06:31 +0530260}
261
Tom Josephcfbd43f2017-03-14 16:14:03 +0530262void EventLoop::stopHostConsole()
263{
Tom Josephcfbd43f2017-03-14 16:14:03 +0530264 if (hostConsole.get())
265 {
266 // Disable the host console payload
Patrick Venturea65e30d2018-10-26 17:55:08 -0700267 int rc = sd_event_source_set_enabled(hostConsole.get(), SD_EVENT_OFF);
Tom Josephcfbd43f2017-03-14 16:14:03 +0530268 if (rc < 0)
269 {
270 log<level::ERR>("Failed to disable the host console socket",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700271 entry("RC=%d", rc));
Tom Josephcfbd43f2017-03-14 16:14:03 +0530272 }
273
274 hostConsole.reset();
275 }
276}
277
Tom Josephe989c362017-03-14 17:16:50 +0530278void EventLoop::startSOLPayloadInstance(uint8_t payloadInst,
279 IntervalType accumulateInterval,
280 IntervalType retryInterval)
281{
282 auto instance = payloadInst;
283 sd_event_source* accTimerSource = nullptr;
284 sd_event_source* retryTimerSource = nullptr;
285 int rc = 0;
286 uint64_t currentTime = 0;
287
288 rc = sd_event_now(event, CLOCK_MONOTONIC, &currentTime);
289 if (rc < 0)
290 {
291 log<level::ERR>("Failed to get the current timestamp",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700292 entry("RC=%d", rc));
Tom Josephe989c362017-03-14 17:16:50 +0530293 throw std::runtime_error("Failed to get current timestamp");
294 }
295
296 // Create character accumulate timer
Vernon Mauery9e801a22018-10-12 13:20:49 -0700297 rc = sd_event_add_time(event, &accTimerSource, CLOCK_MONOTONIC,
298 currentTime + accumulateInterval.count(), 0,
299 charAccTimerHandler, static_cast<void*>(&instance));
Tom Josephe989c362017-03-14 17:16:50 +0530300 if (rc < 0)
301 {
302 log<level::ERR>("Failed to setup the accumulate timer",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700303 entry("RC=%d", rc));
Tom Josephe989c362017-03-14 17:16:50 +0530304 throw std::runtime_error("Failed to setup accumulate timer");
305 }
306
307 // Create retry interval timer and add to the event loop
Vernon Mauery9e801a22018-10-12 13:20:49 -0700308 rc = sd_event_add_time(event, &retryTimerSource, CLOCK_MONOTONIC,
309 currentTime + retryInterval.count(), 0,
310 retryTimerHandler, static_cast<void*>(&instance));
Tom Josephe989c362017-03-14 17:16:50 +0530311 if (rc < 0)
312 {
Vernon Mauery9e801a22018-10-12 13:20:49 -0700313 log<level::ERR>("Failed to setup the retry timer", entry("RC=%d", rc));
Tom Josephe989c362017-03-14 17:16:50 +0530314 throw std::runtime_error("Failed to setup retry timer");
315 }
316
317 // Enable the Character Accumulate Timer
318 rc = sd_event_source_set_enabled(accTimerSource, SD_EVENT_ONESHOT);
319 if (rc < 0)
320 {
321 log<level::ERR>("Failed to enable the accumulate timer",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700322 entry("RC=%d", rc));
Tom Josephe989c362017-03-14 17:16:50 +0530323 throw std::runtime_error("Failed to enable accumulate timer");
324 }
325
326 // Disable the Retry Interval Timer
327 rc = sd_event_source_set_enabled(retryTimerSource, SD_EVENT_OFF);
328 if (rc < 0)
329 {
330 log<level::ERR>("Failed to disable the retry timer",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700331 entry("RC=%d", rc));
Tom Josephe989c362017-03-14 17:16:50 +0530332 throw std::runtime_error("Failed to disable retry timer");
333 }
334
335 EventSource accEventSource(accTimerSource);
336 EventSource retryEventSource(retryTimerSource);
337 accTimerSource = nullptr;
338 retryTimerSource = nullptr;
339
340 TimerMap timer;
341 timer.emplace(Timers::ACCUMULATE, std::make_tuple(std::move(accEventSource),
342 accumulateInterval));
Vernon Mauery9e801a22018-10-12 13:20:49 -0700343 timer.emplace(Timers::RETRY,
344 std::make_tuple(std::move(retryEventSource), retryInterval));
Tom Josephe989c362017-03-14 17:16:50 +0530345 payloadInfo.emplace(instance, std::move(timer));
346}
347
Tom Josepha891eb72017-03-14 17:25:01 +0530348void EventLoop::stopSOLPayloadInstance(uint8_t payloadInst)
349{
350 auto iter = payloadInfo.find(payloadInst);
351 if (iter == payloadInfo.end())
352 {
353 log<level::ERR>("SOL Payload instance not found",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700354 entry("PAYLOADINST=%d", payloadInst));
Tom Josepha891eb72017-03-14 17:25:01 +0530355 throw std::runtime_error("SOL payload instance not found");
356 }
357
358 int rc = 0;
359
360 /* Destroy the character accumulate timer event source */
361 rc = sd_event_source_set_enabled(
Vernon Mauery9e801a22018-10-12 13:20:49 -0700362 (std::get<0>(iter->second.at(Timers::ACCUMULATE))).get(), SD_EVENT_OFF);
Tom Josepha891eb72017-03-14 17:25:01 +0530363 if (rc < 0)
364 {
365 log<level::ERR>("Failed to disable the character accumulate timer",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700366 entry("RC=%d", rc));
Tom Josepha891eb72017-03-14 17:25:01 +0530367 payloadInfo.erase(payloadInst);
368 throw std::runtime_error("Failed to disable accumulate timer");
369 }
370
371 /* Destroy the retry interval timer event source */
372 rc = sd_event_source_set_enabled(
Vernon Mauery9e801a22018-10-12 13:20:49 -0700373 (std::get<0>(iter->second.at(Timers::RETRY))).get(), SD_EVENT_OFF);
Tom Josepha891eb72017-03-14 17:25:01 +0530374 if (rc < 0)
375 {
376 log<level::ERR>("Failed to disable the retry timer",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700377 entry("RC=%d", rc));
Tom Josepha891eb72017-03-14 17:25:01 +0530378 payloadInfo.erase(payloadInst);
379 throw std::runtime_error("Failed to disable retry timer");
380 }
381
382 payloadInfo.erase(payloadInst);
383}
384
Vernon Mauery9e801a22018-10-12 13:20:49 -0700385void EventLoop::switchTimer(uint8_t payloadInst, Timers type, bool status)
Tom Joseph0a0d8f62017-03-14 17:04:58 +0530386{
387 auto iter = payloadInfo.find(payloadInst);
388 if (iter == payloadInfo.end())
389 {
390 log<level::ERR>("SOL Payload instance not found",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700391 entry("PAYLOADINST=%d", payloadInst));
Tom Joseph0a0d8f62017-03-14 17:04:58 +0530392 throw std::runtime_error("SOL Payload instance not found");
393 }
394
395 int rc = 0;
396 auto source = (std::get<0>(iter->second.at(type))).get();
397 auto interval = std::get<1>(iter->second.at(type));
398
399 // Turn OFF the timer
400 if (!status)
401 {
402 rc = sd_event_source_set_enabled(source, SD_EVENT_OFF);
403 if (rc < 0)
404 {
405 log<level::ERR>("Failed to disable the timer", entry("RC=%d", rc));
406 throw std::runtime_error("Failed to disable timer");
407 }
408 return;
409 }
410
411 // Turn ON the timer
412 uint64_t currentTime = 0;
413 rc = sd_event_now(event, CLOCK_MONOTONIC, &currentTime);
414 if (rc < 0)
415 {
416 log<level::ERR>("Failed to get the current timestamp",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700417 entry("RC=%d", rc));
Tom Joseph0a0d8f62017-03-14 17:04:58 +0530418 throw std::runtime_error("Failed to get current timestamp");
419 }
420
421 rc = sd_event_source_set_time(source, currentTime + interval.count());
422 if (rc < 0)
423 {
424 log<level::ERR>("sd_event_source_set_time function failed",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700425 entry("RC=%d", rc));
Tom Joseph0a0d8f62017-03-14 17:04:58 +0530426 throw std::runtime_error("sd_event_source_set_time function failed");
427 }
428
429 rc = sd_event_source_set_enabled(source, SD_EVENT_ONESHOT);
430 if (rc < 0)
431 {
Vernon Mauery9e801a22018-10-12 13:20:49 -0700432 log<level::ERR>("Failed to enable the timer", entry("RC=%d", rc));
Tom Joseph0a0d8f62017-03-14 17:04:58 +0530433 throw std::runtime_error("Failed to enable timer");
434 }
435}
436
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530437} // namespace eventloop