blob: c6ad0634bc1d2b9adb3fba6ab046974b8951066e [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;
Dave Cobbley2c15f0c2017-11-13 16:19:09 -0800179 int listen_fd;
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530180 sigset_t ss;
181 sd_event_source* source = nullptr;
182
Vernon Mauerycbccb052018-10-24 13:52:22 -0700183 sdbusplus::asio::sd_event_wrapper sdEvents(*io);
184 event = sdEvents.get();
Marri Devender Rao3ecf0a12017-07-13 08:07:22 -0500185
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530186 if (sigemptyset(&ss) < 0 || sigaddset(&ss, SIGTERM) < 0 ||
187 sigaddset(&ss, SIGINT) < 0)
188 {
189 r = -errno;
Vernon Mauerycbccb052018-10-24 13:52:22 -0700190 return EXIT_FAILURE;
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530191 }
192
193 /* Block SIGTERM first, so that the event loop can handle it */
194 if (sigprocmask(SIG_BLOCK, &ss, nullptr) < 0)
195 {
196 r = -errno;
Vernon Mauerycbccb052018-10-24 13:52:22 -0700197 return EXIT_FAILURE;
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530198 }
199
200 /* Let's make use of the default handler and "floating" reference features
201 * of sd_event_add_signal() */
202 r = sd_event_add_signal(event, nullptr, SIGTERM, nullptr, nullptr);
203 if (r < 0)
204 {
Vernon Mauerycbccb052018-10-24 13:52:22 -0700205 return EXIT_FAILURE;
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530206 }
207
208 r = sd_event_add_signal(event, nullptr, SIGINT, nullptr, nullptr);
209 if (r < 0)
210 {
Vernon Mauerycbccb052018-10-24 13:52:22 -0700211 return EXIT_FAILURE;
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530212 }
213
Vernon Mauery9e801a22018-10-12 13:20:49 -0700214 // Create our own socket if SysD did not supply one.
Dave Cobbley2c15f0c2017-11-13 16:19:09 -0800215 listen_fd = sd_listen_fds(0);
216 if (listen_fd == 1)
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530217 {
Dave Cobbley2c15f0c2017-11-13 16:19:09 -0800218 fd = SD_LISTEN_FDS_START;
219 }
220 else if (listen_fd > 1)
221 {
222 log<level::ERR>("Too many file descriptors received");
Vernon Mauerycbccb052018-10-24 13:52:22 -0700223 return 1;
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530224 }
Dave Cobbley2c15f0c2017-11-13 16:19:09 -0800225 else
226 {
227 struct sockaddr_in address;
228 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == 0)
229 {
230 r = -errno;
231 log<level::ERR>("Unable to manually open socket");
Vernon Mauerycbccb052018-10-24 13:52:22 -0700232 return EXIT_FAILURE;
Dave Cobbley2c15f0c2017-11-13 16:19:09 -0800233 }
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530234
Dave Cobbley2c15f0c2017-11-13 16:19:09 -0800235 address.sin_family = AF_INET;
236 address.sin_addr.s_addr = INADDR_ANY;
237 address.sin_port = htons(IPMI_STD_PORT);
238
Vernon Mauery9e801a22018-10-12 13:20:49 -0700239 if (bind(fd, (struct sockaddr*)&address, sizeof(address)) < 0)
Dave Cobbley2c15f0c2017-11-13 16:19:09 -0800240 {
241 r = -errno;
242 log<level::ERR>("Unable to bind socket");
Vernon Mauerycbccb052018-10-24 13:52:22 -0700243 close(fd);
244 return EXIT_FAILURE;
Dave Cobbley2c15f0c2017-11-13 16:19:09 -0800245 }
246 }
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530247
248 r = sd_event_add_io(event, &source, fd, EPOLLIN, udp623Handler, nullptr);
249 if (r < 0)
250 {
Vernon Mauerycbccb052018-10-24 13:52:22 -0700251 close(fd);
252 return EXIT_FAILURE;
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530253 }
254
255 udpIPMI.reset(source);
256 source = nullptr;
257
Vernon Mauerycbccb052018-10-24 13:52:22 -0700258 io->run();
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530259
Vernon Mauerycbccb052018-10-24 13:52:22 -0700260 return EXIT_SUCCESS;
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530261}
262
Tom Joseph56f24e92017-03-14 16:06:31 +0530263void EventLoop::startHostConsole(const sol::CustomFD& fd)
264{
265 int rc = 0;
266
Vernon Mauery9e801a22018-10-12 13:20:49 -0700267 if ((fd() == -1) || hostConsole.get())
Tom Joseph56f24e92017-03-14 16:06:31 +0530268 {
269 throw std::runtime_error("Console descriptor already added");
270 }
271
272 sd_event_source* source = nullptr;
273
274 // Add the fd to the event loop for EPOLLIN
Vernon Mauery9e801a22018-10-12 13:20:49 -0700275 rc = sd_event_add_io(event, &source, fd(), EPOLLIN, consoleInputHandler,
276 nullptr);
Tom Joseph56f24e92017-03-14 16:06:31 +0530277 if (rc < 0)
278 {
279 throw std::runtime_error("Failed to add socket descriptor");
280 }
281
282 hostConsole.reset(source);
Vernon Mauery9e801a22018-10-12 13:20:49 -0700283 source = nullptr;
Tom Joseph56f24e92017-03-14 16:06:31 +0530284}
285
Tom Josephcfbd43f2017-03-14 16:14:03 +0530286void EventLoop::stopHostConsole()
287{
Tom Josephcfbd43f2017-03-14 16:14:03 +0530288 if (hostConsole.get())
289 {
290 // Disable the host console payload
Patrick Venturea65e30d2018-10-26 17:55:08 -0700291 int rc = sd_event_source_set_enabled(hostConsole.get(), SD_EVENT_OFF);
Tom Josephcfbd43f2017-03-14 16:14:03 +0530292 if (rc < 0)
293 {
294 log<level::ERR>("Failed to disable the host console socket",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700295 entry("RC=%d", rc));
Tom Josephcfbd43f2017-03-14 16:14:03 +0530296 }
297
298 hostConsole.reset();
299 }
300}
301
Tom Josephe989c362017-03-14 17:16:50 +0530302void EventLoop::startSOLPayloadInstance(uint8_t payloadInst,
303 IntervalType accumulateInterval,
304 IntervalType retryInterval)
305{
306 auto instance = payloadInst;
307 sd_event_source* accTimerSource = nullptr;
308 sd_event_source* retryTimerSource = nullptr;
309 int rc = 0;
310 uint64_t currentTime = 0;
311
312 rc = sd_event_now(event, CLOCK_MONOTONIC, &currentTime);
313 if (rc < 0)
314 {
315 log<level::ERR>("Failed to get the current timestamp",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700316 entry("RC=%d", rc));
Tom Josephe989c362017-03-14 17:16:50 +0530317 throw std::runtime_error("Failed to get current timestamp");
318 }
319
320 // Create character accumulate timer
Vernon Mauery9e801a22018-10-12 13:20:49 -0700321 rc = sd_event_add_time(event, &accTimerSource, CLOCK_MONOTONIC,
322 currentTime + accumulateInterval.count(), 0,
323 charAccTimerHandler, static_cast<void*>(&instance));
Tom Josephe989c362017-03-14 17:16:50 +0530324 if (rc < 0)
325 {
326 log<level::ERR>("Failed to setup the accumulate timer",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700327 entry("RC=%d", rc));
Tom Josephe989c362017-03-14 17:16:50 +0530328 throw std::runtime_error("Failed to setup accumulate timer");
329 }
330
331 // Create retry interval timer and add to the event loop
Vernon Mauery9e801a22018-10-12 13:20:49 -0700332 rc = sd_event_add_time(event, &retryTimerSource, CLOCK_MONOTONIC,
333 currentTime + retryInterval.count(), 0,
334 retryTimerHandler, static_cast<void*>(&instance));
Tom Josephe989c362017-03-14 17:16:50 +0530335 if (rc < 0)
336 {
Vernon Mauery9e801a22018-10-12 13:20:49 -0700337 log<level::ERR>("Failed to setup the retry timer", entry("RC=%d", rc));
Tom Josephe989c362017-03-14 17:16:50 +0530338 throw std::runtime_error("Failed to setup retry timer");
339 }
340
341 // Enable the Character Accumulate Timer
342 rc = sd_event_source_set_enabled(accTimerSource, SD_EVENT_ONESHOT);
343 if (rc < 0)
344 {
345 log<level::ERR>("Failed to enable the accumulate timer",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700346 entry("RC=%d", rc));
Tom Josephe989c362017-03-14 17:16:50 +0530347 throw std::runtime_error("Failed to enable accumulate timer");
348 }
349
350 // Disable the Retry Interval Timer
351 rc = sd_event_source_set_enabled(retryTimerSource, SD_EVENT_OFF);
352 if (rc < 0)
353 {
354 log<level::ERR>("Failed to disable the retry timer",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700355 entry("RC=%d", rc));
Tom Josephe989c362017-03-14 17:16:50 +0530356 throw std::runtime_error("Failed to disable retry timer");
357 }
358
359 EventSource accEventSource(accTimerSource);
360 EventSource retryEventSource(retryTimerSource);
361 accTimerSource = nullptr;
362 retryTimerSource = nullptr;
363
364 TimerMap timer;
365 timer.emplace(Timers::ACCUMULATE, std::make_tuple(std::move(accEventSource),
366 accumulateInterval));
Vernon Mauery9e801a22018-10-12 13:20:49 -0700367 timer.emplace(Timers::RETRY,
368 std::make_tuple(std::move(retryEventSource), retryInterval));
Tom Josephe989c362017-03-14 17:16:50 +0530369 payloadInfo.emplace(instance, std::move(timer));
370}
371
Tom Josepha891eb72017-03-14 17:25:01 +0530372void EventLoop::stopSOLPayloadInstance(uint8_t payloadInst)
373{
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 Josepha891eb72017-03-14 17:25:01 +0530379 throw std::runtime_error("SOL payload instance not found");
380 }
381
382 int rc = 0;
383
384 /* Destroy the character accumulate timer event source */
385 rc = sd_event_source_set_enabled(
Vernon Mauery9e801a22018-10-12 13:20:49 -0700386 (std::get<0>(iter->second.at(Timers::ACCUMULATE))).get(), SD_EVENT_OFF);
Tom Josepha891eb72017-03-14 17:25:01 +0530387 if (rc < 0)
388 {
389 log<level::ERR>("Failed to disable the character accumulate timer",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700390 entry("RC=%d", rc));
Tom Josepha891eb72017-03-14 17:25:01 +0530391 payloadInfo.erase(payloadInst);
392 throw std::runtime_error("Failed to disable accumulate timer");
393 }
394
395 /* Destroy the retry interval timer event source */
396 rc = sd_event_source_set_enabled(
Vernon Mauery9e801a22018-10-12 13:20:49 -0700397 (std::get<0>(iter->second.at(Timers::RETRY))).get(), SD_EVENT_OFF);
Tom Josepha891eb72017-03-14 17:25:01 +0530398 if (rc < 0)
399 {
400 log<level::ERR>("Failed to disable the retry timer",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700401 entry("RC=%d", rc));
Tom Josepha891eb72017-03-14 17:25:01 +0530402 payloadInfo.erase(payloadInst);
403 throw std::runtime_error("Failed to disable retry timer");
404 }
405
406 payloadInfo.erase(payloadInst);
407}
408
Vernon Mauery9e801a22018-10-12 13:20:49 -0700409void EventLoop::switchTimer(uint8_t payloadInst, Timers type, bool status)
Tom Joseph0a0d8f62017-03-14 17:04:58 +0530410{
411 auto iter = payloadInfo.find(payloadInst);
412 if (iter == payloadInfo.end())
413 {
414 log<level::ERR>("SOL Payload instance not found",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700415 entry("PAYLOADINST=%d", payloadInst));
Tom Joseph0a0d8f62017-03-14 17:04:58 +0530416 throw std::runtime_error("SOL Payload instance not found");
417 }
418
419 int rc = 0;
420 auto source = (std::get<0>(iter->second.at(type))).get();
421 auto interval = std::get<1>(iter->second.at(type));
422
423 // Turn OFF the timer
424 if (!status)
425 {
426 rc = sd_event_source_set_enabled(source, SD_EVENT_OFF);
427 if (rc < 0)
428 {
429 log<level::ERR>("Failed to disable the timer", entry("RC=%d", rc));
430 throw std::runtime_error("Failed to disable timer");
431 }
432 return;
433 }
434
435 // Turn ON the timer
436 uint64_t currentTime = 0;
437 rc = sd_event_now(event, CLOCK_MONOTONIC, &currentTime);
438 if (rc < 0)
439 {
440 log<level::ERR>("Failed to get the current timestamp",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700441 entry("RC=%d", rc));
Tom Joseph0a0d8f62017-03-14 17:04:58 +0530442 throw std::runtime_error("Failed to get current timestamp");
443 }
444
445 rc = sd_event_source_set_time(source, currentTime + interval.count());
446 if (rc < 0)
447 {
448 log<level::ERR>("sd_event_source_set_time function failed",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700449 entry("RC=%d", rc));
Tom Joseph0a0d8f62017-03-14 17:04:58 +0530450 throw std::runtime_error("sd_event_source_set_time function failed");
451 }
452
453 rc = sd_event_source_set_enabled(source, SD_EVENT_ONESHOT);
454 if (rc < 0)
455 {
Vernon Mauery9e801a22018-10-12 13:20:49 -0700456 log<level::ERR>("Failed to enable the timer", entry("RC=%d", rc));
Tom Joseph0a0d8f62017-03-14 17:04:58 +0530457 throw std::runtime_error("Failed to enable timer");
458 }
459}
460
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530461} // namespace eventloop