blob: 7ec4db9c8e4d21510693dde7969ad5ce89bc0a33 [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
Tom Joseph7fd26dd2017-03-14 15:26:26 +053011#include <phosphor-logging/log.hpp>
Tom Joseph7fd26dd2017-03-14 15:26:26 +053012
13namespace eventloop
14{
15using namespace phosphor::logging;
16
17static int udp623Handler(sd_event_source* es, int fd, uint32_t revents,
18 void* userdata)
19{
20 std::shared_ptr<udpsocket::Channel> channelPtr;
21 struct timeval timeout;
22 timeout.tv_sec = SELECT_CALL_TIMEOUT;
23 timeout.tv_usec = 0;
24
25 try
26 {
27 channelPtr.reset(new udpsocket::Channel(fd, timeout));
28
29 // Initialize the Message Handler with the socket channel
30 message::Handler msgHandler(channelPtr);
31
Tom Joseph7fd26dd2017-03-14 15:26:26 +053032 std::unique_ptr<message::Message> inMessage;
33
34 // Read the incoming IPMI packet
35 inMessage = msgHandler.receive();
36 if (inMessage == nullptr)
37 {
38 return 0;
39 }
40
41 // Execute the Command
42 auto outMessage = msgHandler.executeCommand(*(inMessage.get()));
43 if (outMessage == nullptr)
44 {
45 return 0;
46 }
47
48 // Send the response IPMI Message
49 msgHandler.send(*(outMessage.get()));
50 }
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{
107 // The instance is hardcoded to 1, in the case of supporting multiple
108 // payload instances we would need to populate it from userdata
109 uint8_t instance = 1;
Tom Joseph73063142017-03-14 18:20:20 +0530110 int rc = 0;
Tom Josephe989c362017-03-14 17:16:50 +0530111 auto bufferSize = std::get<sol::Manager&>(singletonPool).dataBuffer.size();
112
113 try
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
120 rc = context.sendOutboundPayload();
121
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{
141 // The instance is hardcoded to 1, in the case of supporting multiple
142 // payload instances we would need to populate it from userdata
143 uint8_t instance = 1;
144
145 try
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
Ratan Gupta166c71a2018-03-23 23:05:17 +0530175int EventLoop::startEventLoop(sd_event* events)
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;
Marri Devender Rao3ecf0a12017-07-13 08:07:22 -0500182 auto bus = ipmid_get_sd_bus_connection();
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530183
Ratan Gupta166c71a2018-03-23 23:05:17 +0530184 event = events;
Marri Devender Rao3ecf0a12017-07-13 08:07:22 -0500185 // Attach the bus to sd_event to service user requests
186 r = sd_bus_attach_event(bus, event, SD_EVENT_PRIORITY_NORMAL);
187 if (r < 0)
188 {
189 goto finish;
190 }
191
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530192 if (sigemptyset(&ss) < 0 || sigaddset(&ss, SIGTERM) < 0 ||
193 sigaddset(&ss, SIGINT) < 0)
194 {
195 r = -errno;
196 goto finish;
197 }
198
199 /* Block SIGTERM first, so that the event loop can handle it */
200 if (sigprocmask(SIG_BLOCK, &ss, nullptr) < 0)
201 {
202 r = -errno;
203 goto finish;
204 }
205
206 /* Let's make use of the default handler and "floating" reference features
207 * of sd_event_add_signal() */
208 r = sd_event_add_signal(event, nullptr, SIGTERM, nullptr, nullptr);
209 if (r < 0)
210 {
211 goto finish;
212 }
213
214 r = sd_event_add_signal(event, nullptr, SIGINT, nullptr, nullptr);
215 if (r < 0)
216 {
217 goto finish;
218 }
219
Vernon Mauery9e801a22018-10-12 13:20:49 -0700220 // Create our own socket if SysD did not supply one.
Dave Cobbley2c15f0c2017-11-13 16:19:09 -0800221 listen_fd = sd_listen_fds(0);
222 if (listen_fd == 1)
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530223 {
Dave Cobbley2c15f0c2017-11-13 16:19:09 -0800224 fd = SD_LISTEN_FDS_START;
225 }
226 else if (listen_fd > 1)
227 {
228 log<level::ERR>("Too many file descriptors received");
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530229 goto finish;
230 }
Dave Cobbley2c15f0c2017-11-13 16:19:09 -0800231 else
232 {
233 struct sockaddr_in address;
234 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == 0)
235 {
236 r = -errno;
237 log<level::ERR>("Unable to manually open socket");
238 goto finish;
239 }
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530240
Dave Cobbley2c15f0c2017-11-13 16:19:09 -0800241 address.sin_family = AF_INET;
242 address.sin_addr.s_addr = INADDR_ANY;
243 address.sin_port = htons(IPMI_STD_PORT);
244
Vernon Mauery9e801a22018-10-12 13:20:49 -0700245 if (bind(fd, (struct sockaddr*)&address, sizeof(address)) < 0)
Dave Cobbley2c15f0c2017-11-13 16:19:09 -0800246 {
247 r = -errno;
248 log<level::ERR>("Unable to bind socket");
249 goto finish;
250 }
251 }
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530252
253 r = sd_event_add_io(event, &source, fd, EPOLLIN, udp623Handler, nullptr);
254 if (r < 0)
255 {
256 goto finish;
257 }
258
259 udpIPMI.reset(source);
260 source = nullptr;
261
262 r = sd_event_loop(event);
263
264finish:
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530265
266 if (fd >= 0)
267 {
Vernon Mauery9e801a22018-10-12 13:20:49 -0700268 (void)close(fd);
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530269 }
270
271 if (r < 0)
272 {
273 log<level::ERR>("Event Loop Failure:",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700274 entry("FAILURE=%s", strerror(-r)));
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530275 }
276
277 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
278}
279
Tom Joseph56f24e92017-03-14 16:06:31 +0530280void EventLoop::startHostConsole(const sol::CustomFD& fd)
281{
282 int rc = 0;
283
Vernon Mauery9e801a22018-10-12 13:20:49 -0700284 if ((fd() == -1) || hostConsole.get())
Tom Joseph56f24e92017-03-14 16:06:31 +0530285 {
286 throw std::runtime_error("Console descriptor already added");
287 }
288
289 sd_event_source* source = nullptr;
290
291 // Add the fd to the event loop for EPOLLIN
Vernon Mauery9e801a22018-10-12 13:20:49 -0700292 rc = sd_event_add_io(event, &source, fd(), EPOLLIN, consoleInputHandler,
293 nullptr);
Tom Joseph56f24e92017-03-14 16:06:31 +0530294 if (rc < 0)
295 {
296 throw std::runtime_error("Failed to add socket descriptor");
297 }
298
299 hostConsole.reset(source);
Vernon Mauery9e801a22018-10-12 13:20:49 -0700300 source = nullptr;
Tom Joseph56f24e92017-03-14 16:06:31 +0530301}
302
Tom Josephcfbd43f2017-03-14 16:14:03 +0530303void EventLoop::stopHostConsole()
304{
305 int rc = 0;
306
307 if (hostConsole.get())
308 {
309 // Disable the host console payload
310 rc = sd_event_source_set_enabled(hostConsole.get(), SD_EVENT_OFF);
311 if (rc < 0)
312 {
313 log<level::ERR>("Failed to disable the host console socket",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700314 entry("RC=%d", rc));
Tom Josephcfbd43f2017-03-14 16:14:03 +0530315 }
316
317 hostConsole.reset();
318 }
319}
320
Tom Josephe989c362017-03-14 17:16:50 +0530321void EventLoop::startSOLPayloadInstance(uint8_t payloadInst,
322 IntervalType accumulateInterval,
323 IntervalType retryInterval)
324{
325 auto instance = payloadInst;
326 sd_event_source* accTimerSource = nullptr;
327 sd_event_source* retryTimerSource = nullptr;
328 int rc = 0;
329 uint64_t currentTime = 0;
330
331 rc = sd_event_now(event, CLOCK_MONOTONIC, &currentTime);
332 if (rc < 0)
333 {
334 log<level::ERR>("Failed to get the current timestamp",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700335 entry("RC=%d", rc));
Tom Josephe989c362017-03-14 17:16:50 +0530336 throw std::runtime_error("Failed to get current timestamp");
337 }
338
339 // Create character accumulate timer
Vernon Mauery9e801a22018-10-12 13:20:49 -0700340 rc = sd_event_add_time(event, &accTimerSource, CLOCK_MONOTONIC,
341 currentTime + accumulateInterval.count(), 0,
342 charAccTimerHandler, static_cast<void*>(&instance));
Tom Josephe989c362017-03-14 17:16:50 +0530343 if (rc < 0)
344 {
345 log<level::ERR>("Failed to setup 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 setup accumulate timer");
348 }
349
350 // Create retry interval timer and add to the event loop
Vernon Mauery9e801a22018-10-12 13:20:49 -0700351 rc = sd_event_add_time(event, &retryTimerSource, CLOCK_MONOTONIC,
352 currentTime + retryInterval.count(), 0,
353 retryTimerHandler, static_cast<void*>(&instance));
Tom Josephe989c362017-03-14 17:16:50 +0530354 if (rc < 0)
355 {
Vernon Mauery9e801a22018-10-12 13:20:49 -0700356 log<level::ERR>("Failed to setup the retry timer", entry("RC=%d", rc));
Tom Josephe989c362017-03-14 17:16:50 +0530357 throw std::runtime_error("Failed to setup retry timer");
358 }
359
360 // Enable the Character Accumulate Timer
361 rc = sd_event_source_set_enabled(accTimerSource, SD_EVENT_ONESHOT);
362 if (rc < 0)
363 {
364 log<level::ERR>("Failed to enable the accumulate timer",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700365 entry("RC=%d", rc));
Tom Josephe989c362017-03-14 17:16:50 +0530366 throw std::runtime_error("Failed to enable accumulate timer");
367 }
368
369 // Disable the Retry Interval Timer
370 rc = sd_event_source_set_enabled(retryTimerSource, SD_EVENT_OFF);
371 if (rc < 0)
372 {
373 log<level::ERR>("Failed to disable the retry timer",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700374 entry("RC=%d", rc));
Tom Josephe989c362017-03-14 17:16:50 +0530375 throw std::runtime_error("Failed to disable retry timer");
376 }
377
378 EventSource accEventSource(accTimerSource);
379 EventSource retryEventSource(retryTimerSource);
380 accTimerSource = nullptr;
381 retryTimerSource = nullptr;
382
383 TimerMap timer;
384 timer.emplace(Timers::ACCUMULATE, std::make_tuple(std::move(accEventSource),
385 accumulateInterval));
Vernon Mauery9e801a22018-10-12 13:20:49 -0700386 timer.emplace(Timers::RETRY,
387 std::make_tuple(std::move(retryEventSource), retryInterval));
Tom Josephe989c362017-03-14 17:16:50 +0530388 payloadInfo.emplace(instance, std::move(timer));
389}
390
Tom Josepha891eb72017-03-14 17:25:01 +0530391void EventLoop::stopSOLPayloadInstance(uint8_t payloadInst)
392{
393 auto iter = payloadInfo.find(payloadInst);
394 if (iter == payloadInfo.end())
395 {
396 log<level::ERR>("SOL Payload instance not found",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700397 entry("PAYLOADINST=%d", payloadInst));
Tom Josepha891eb72017-03-14 17:25:01 +0530398 throw std::runtime_error("SOL payload instance not found");
399 }
400
401 int rc = 0;
402
403 /* Destroy the character accumulate timer event source */
404 rc = sd_event_source_set_enabled(
Vernon Mauery9e801a22018-10-12 13:20:49 -0700405 (std::get<0>(iter->second.at(Timers::ACCUMULATE))).get(), SD_EVENT_OFF);
Tom Josepha891eb72017-03-14 17:25:01 +0530406 if (rc < 0)
407 {
408 log<level::ERR>("Failed to disable the character accumulate timer",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700409 entry("RC=%d", rc));
Tom Josepha891eb72017-03-14 17:25:01 +0530410 payloadInfo.erase(payloadInst);
411 throw std::runtime_error("Failed to disable accumulate timer");
412 }
413
414 /* Destroy the retry interval timer event source */
415 rc = sd_event_source_set_enabled(
Vernon Mauery9e801a22018-10-12 13:20:49 -0700416 (std::get<0>(iter->second.at(Timers::RETRY))).get(), SD_EVENT_OFF);
Tom Josepha891eb72017-03-14 17:25:01 +0530417 if (rc < 0)
418 {
419 log<level::ERR>("Failed to disable the retry timer",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700420 entry("RC=%d", rc));
Tom Josepha891eb72017-03-14 17:25:01 +0530421 payloadInfo.erase(payloadInst);
422 throw std::runtime_error("Failed to disable retry timer");
423 }
424
425 payloadInfo.erase(payloadInst);
426}
427
Vernon Mauery9e801a22018-10-12 13:20:49 -0700428void EventLoop::switchTimer(uint8_t payloadInst, Timers type, bool status)
Tom Joseph0a0d8f62017-03-14 17:04:58 +0530429{
430 auto iter = payloadInfo.find(payloadInst);
431 if (iter == payloadInfo.end())
432 {
433 log<level::ERR>("SOL Payload instance not found",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700434 entry("PAYLOADINST=%d", payloadInst));
Tom Joseph0a0d8f62017-03-14 17:04:58 +0530435 throw std::runtime_error("SOL Payload instance not found");
436 }
437
438 int rc = 0;
439 auto source = (std::get<0>(iter->second.at(type))).get();
440 auto interval = std::get<1>(iter->second.at(type));
441
442 // Turn OFF the timer
443 if (!status)
444 {
445 rc = sd_event_source_set_enabled(source, SD_EVENT_OFF);
446 if (rc < 0)
447 {
448 log<level::ERR>("Failed to disable the timer", entry("RC=%d", rc));
449 throw std::runtime_error("Failed to disable timer");
450 }
451 return;
452 }
453
454 // Turn ON the timer
455 uint64_t currentTime = 0;
456 rc = sd_event_now(event, CLOCK_MONOTONIC, &currentTime);
457 if (rc < 0)
458 {
459 log<level::ERR>("Failed to get the current timestamp",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700460 entry("RC=%d", rc));
Tom Joseph0a0d8f62017-03-14 17:04:58 +0530461 throw std::runtime_error("Failed to get current timestamp");
462 }
463
464 rc = sd_event_source_set_time(source, currentTime + interval.count());
465 if (rc < 0)
466 {
467 log<level::ERR>("sd_event_source_set_time function failed",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700468 entry("RC=%d", rc));
Tom Joseph0a0d8f62017-03-14 17:04:58 +0530469 throw std::runtime_error("sd_event_source_set_time function failed");
470 }
471
472 rc = sd_event_source_set_enabled(source, SD_EVENT_ONESHOT);
473 if (rc < 0)
474 {
Vernon Mauery9e801a22018-10-12 13:20:49 -0700475 log<level::ERR>("Failed to enable the timer", entry("RC=%d", rc));
Tom Joseph0a0d8f62017-03-14 17:04:58 +0530476 throw std::runtime_error("Failed to enable timer");
477 }
478}
479
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530480} // namespace eventloop