blob: aa5224ac29fbba8aab95b4a99706af7deeb612ec [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 // Read the incoming IPMI packet
Vernon Maueryd999ffc2018-10-25 09:16:05 -070033 std::shared_ptr<message::Message> inMessage(msgHandler.receive());
Tom Joseph7fd26dd2017-03-14 15:26:26 +053034 if (inMessage == nullptr)
35 {
36 return 0;
37 }
38
39 // Execute the Command
Vernon Maueryd999ffc2018-10-25 09:16:05 -070040 auto outMessage = msgHandler.executeCommand(inMessage);
Tom Joseph7fd26dd2017-03-14 15:26:26 +053041 if (outMessage == nullptr)
42 {
43 return 0;
44 }
45
46 // Send the response IPMI Message
Vernon Maueryd999ffc2018-10-25 09:16:05 -070047 msgHandler.send(outMessage);
Tom Joseph7fd26dd2017-03-14 15:26:26 +053048 }
49 catch (std::exception& e)
50 {
51 log<level::ERR>("Executing the IPMI message failed");
52 log<level::ERR>(e.what());
53 }
54
55 return 0;
56}
57
Tom Joseph56f24e92017-03-14 16:06:31 +053058static int consoleInputHandler(sd_event_source* es, int fd, uint32_t revents,
59 void* userdata)
60{
61 try
62 {
63 int readSize = 0;
64
65 if (ioctl(fd, FIONREAD, &readSize) < 0)
66 {
67 log<level::ERR>("ioctl failed for FIONREAD:",
Vernon Mauery9e801a22018-10-12 13:20:49 -070068 entry("ERRNO=%d", errno));
Tom Joseph56f24e92017-03-14 16:06:31 +053069 return 0;
70 }
71
72 std::vector<uint8_t> buffer(readSize);
73 auto bufferSize = buffer.size();
74 ssize_t readDataLen = 0;
75
76 readDataLen = read(fd, buffer.data(), bufferSize);
77
78 // Update the Console buffer with data read from the socket
79 if (readDataLen > 0)
80 {
81 buffer.resize(readDataLen);
82 std::get<sol::Manager&>(singletonPool).dataBuffer.write(buffer);
83 }
84 else if (readDataLen == 0)
85 {
86 log<level::ERR>("Connection Closed for host console socket");
87 }
88 else if (readDataLen < 0) // Error
89 {
90 log<level::ERR>("Reading from host console socket failed:",
Vernon Mauery9e801a22018-10-12 13:20:49 -070091 entry("ERRNO=%d", errno));
Tom Joseph56f24e92017-03-14 16:06:31 +053092 }
93 }
94 catch (std::exception& e)
95 {
96 log<level::ERR>(e.what());
97 }
98
99 return 0;
100}
101
Tom Josephe989c362017-03-14 17:16:50 +0530102static int charAccTimerHandler(sd_event_source* s, uint64_t usec,
Vernon Mauery9e801a22018-10-12 13:20:49 -0700103 void* userdata)
Tom Josephe989c362017-03-14 17:16:50 +0530104{
Tom Josephe989c362017-03-14 17:16:50 +0530105 auto bufferSize = std::get<sol::Manager&>(singletonPool).dataBuffer.size();
106
107 try
108 {
Patrick Venturea65e30d2018-10-26 17:55:08 -0700109 // The instance is hardcoded to 1, in the case of supporting multiple
110 // payload instances we would need to populate it from userdata
111 uint8_t instance = 1;
112
Vernon Mauery9e801a22018-10-12 13:20:49 -0700113 if (bufferSize > 0)
Tom Josephe989c362017-03-14 17:16:50 +0530114 {
Vernon Mauery9e801a22018-10-12 13:20:49 -0700115 auto& context =
116 std::get<sol::Manager&>(singletonPool).getContext(instance);
Tom Joseph73063142017-03-14 18:20:20 +0530117
Patrick Venturea65e30d2018-10-26 17:55:08 -0700118 int rc = context.sendOutboundPayload();
Tom Joseph73063142017-03-14 18:20:20 +0530119
120 if (rc == 0)
121 {
122 return 0;
123 }
Tom Josephe989c362017-03-14 17:16:50 +0530124 }
125
Vernon Mauery9e801a22018-10-12 13:20:49 -0700126 std::get<eventloop::EventLoop&>(singletonPool)
127 .switchTimer(instance, Timers::ACCUMULATE, true);
Tom Josephe989c362017-03-14 17:16:50 +0530128 }
129 catch (std::exception& e)
130 {
131 log<level::ERR>(e.what());
132 }
133
134 return 0;
135}
136
Vernon Mauery9e801a22018-10-12 13:20:49 -0700137static int retryTimerHandler(sd_event_source* s, uint64_t usec, void* userdata)
Tom Josephe989c362017-03-14 17:16:50 +0530138{
Tom Josephe989c362017-03-14 17:16:50 +0530139 try
140 {
Patrick Venturea65e30d2018-10-26 17:55:08 -0700141 // 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
Vernon Mauery9e801a22018-10-12 13:20:49 -0700145 auto& context =
146 std::get<sol::Manager&>(singletonPool).getContext(instance);
Tom Josephe989c362017-03-14 17:16:50 +0530147
148 if (context.retryCounter)
149 {
150 --context.retryCounter;
Vernon Mauery9e801a22018-10-12 13:20:49 -0700151 std::get<eventloop::EventLoop&>(singletonPool)
152 .switchTimer(instance, Timers::RETRY, true);
Tom Joseph2fd466f2017-03-30 08:16:47 +0530153 context.resendPayload(sol::Context::noClear);
Tom Josephe989c362017-03-14 17:16:50 +0530154 }
155 else
156 {
157 context.retryCounter = context.maxRetryCount;
Tom Joseph2fd466f2017-03-30 08:16:47 +0530158 context.resendPayload(sol::Context::clear);
Vernon Mauery9e801a22018-10-12 13:20:49 -0700159 std::get<eventloop::EventLoop&>(singletonPool)
160 .switchTimer(instance, Timers::RETRY, false);
161 std::get<eventloop::EventLoop&>(singletonPool)
162 .switchTimer(instance, Timers::ACCUMULATE, true);
Tom Josephe989c362017-03-14 17:16:50 +0530163 }
164 }
165 catch (std::exception& e)
166 {
167 log<level::ERR>(e.what());
168 }
169
170 return 0;
171}
172
Ratan Gupta166c71a2018-03-23 23:05:17 +0530173int EventLoop::startEventLoop(sd_event* events)
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530174{
175 int fd = -1;
176 int r = 0;
Dave Cobbley2c15f0c2017-11-13 16:19:09 -0800177 int listen_fd;
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530178 sigset_t ss;
179 sd_event_source* source = nullptr;
Marri Devender Rao3ecf0a12017-07-13 08:07:22 -0500180 auto bus = ipmid_get_sd_bus_connection();
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530181
Ratan Gupta166c71a2018-03-23 23:05:17 +0530182 event = events;
Marri Devender Rao3ecf0a12017-07-13 08:07:22 -0500183 // Attach the bus to sd_event to service user requests
184 r = sd_bus_attach_event(bus, event, SD_EVENT_PRIORITY_NORMAL);
185 if (r < 0)
186 {
187 goto finish;
188 }
189
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530190 if (sigemptyset(&ss) < 0 || sigaddset(&ss, SIGTERM) < 0 ||
191 sigaddset(&ss, SIGINT) < 0)
192 {
193 r = -errno;
194 goto finish;
195 }
196
197 /* Block SIGTERM first, so that the event loop can handle it */
198 if (sigprocmask(SIG_BLOCK, &ss, nullptr) < 0)
199 {
200 r = -errno;
201 goto finish;
202 }
203
204 /* Let's make use of the default handler and "floating" reference features
205 * of sd_event_add_signal() */
206 r = sd_event_add_signal(event, nullptr, SIGTERM, nullptr, nullptr);
207 if (r < 0)
208 {
209 goto finish;
210 }
211
212 r = sd_event_add_signal(event, nullptr, SIGINT, nullptr, nullptr);
213 if (r < 0)
214 {
215 goto finish;
216 }
217
Vernon Mauery9e801a22018-10-12 13:20:49 -0700218 // Create our own socket if SysD did not supply one.
Dave Cobbley2c15f0c2017-11-13 16:19:09 -0800219 listen_fd = sd_listen_fds(0);
220 if (listen_fd == 1)
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530221 {
Dave Cobbley2c15f0c2017-11-13 16:19:09 -0800222 fd = SD_LISTEN_FDS_START;
223 }
224 else if (listen_fd > 1)
225 {
226 log<level::ERR>("Too many file descriptors received");
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530227 goto finish;
228 }
Dave Cobbley2c15f0c2017-11-13 16:19:09 -0800229 else
230 {
231 struct sockaddr_in address;
232 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == 0)
233 {
234 r = -errno;
235 log<level::ERR>("Unable to manually open socket");
236 goto finish;
237 }
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530238
Dave Cobbley2c15f0c2017-11-13 16:19:09 -0800239 address.sin_family = AF_INET;
240 address.sin_addr.s_addr = INADDR_ANY;
241 address.sin_port = htons(IPMI_STD_PORT);
242
Vernon Mauery9e801a22018-10-12 13:20:49 -0700243 if (bind(fd, (struct sockaddr*)&address, sizeof(address)) < 0)
Dave Cobbley2c15f0c2017-11-13 16:19:09 -0800244 {
245 r = -errno;
246 log<level::ERR>("Unable to bind socket");
247 goto finish;
248 }
249 }
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530250
251 r = sd_event_add_io(event, &source, fd, EPOLLIN, udp623Handler, nullptr);
252 if (r < 0)
253 {
254 goto finish;
255 }
256
257 udpIPMI.reset(source);
258 source = nullptr;
259
260 r = sd_event_loop(event);
261
262finish:
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530263
264 if (fd >= 0)
265 {
Vernon Mauery9e801a22018-10-12 13:20:49 -0700266 (void)close(fd);
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530267 }
268
269 if (r < 0)
270 {
271 log<level::ERR>("Event Loop Failure:",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700272 entry("FAILURE=%s", strerror(-r)));
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530273 }
274
275 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
276}
277
Tom Joseph56f24e92017-03-14 16:06:31 +0530278void EventLoop::startHostConsole(const sol::CustomFD& fd)
279{
280 int rc = 0;
281
Vernon Mauery9e801a22018-10-12 13:20:49 -0700282 if ((fd() == -1) || hostConsole.get())
Tom Joseph56f24e92017-03-14 16:06:31 +0530283 {
284 throw std::runtime_error("Console descriptor already added");
285 }
286
287 sd_event_source* source = nullptr;
288
289 // Add the fd to the event loop for EPOLLIN
Vernon Mauery9e801a22018-10-12 13:20:49 -0700290 rc = sd_event_add_io(event, &source, fd(), EPOLLIN, consoleInputHandler,
291 nullptr);
Tom Joseph56f24e92017-03-14 16:06:31 +0530292 if (rc < 0)
293 {
294 throw std::runtime_error("Failed to add socket descriptor");
295 }
296
297 hostConsole.reset(source);
Vernon Mauery9e801a22018-10-12 13:20:49 -0700298 source = nullptr;
Tom Joseph56f24e92017-03-14 16:06:31 +0530299}
300
Tom Josephcfbd43f2017-03-14 16:14:03 +0530301void EventLoop::stopHostConsole()
302{
Tom Josephcfbd43f2017-03-14 16:14:03 +0530303 if (hostConsole.get())
304 {
305 // Disable the host console payload
Patrick Venturea65e30d2018-10-26 17:55:08 -0700306 int rc = sd_event_source_set_enabled(hostConsole.get(), SD_EVENT_OFF);
Tom Josephcfbd43f2017-03-14 16:14:03 +0530307 if (rc < 0)
308 {
309 log<level::ERR>("Failed to disable the host console socket",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700310 entry("RC=%d", rc));
Tom Josephcfbd43f2017-03-14 16:14:03 +0530311 }
312
313 hostConsole.reset();
314 }
315}
316
Tom Josephe989c362017-03-14 17:16:50 +0530317void EventLoop::startSOLPayloadInstance(uint8_t payloadInst,
318 IntervalType accumulateInterval,
319 IntervalType retryInterval)
320{
321 auto instance = payloadInst;
322 sd_event_source* accTimerSource = nullptr;
323 sd_event_source* retryTimerSource = nullptr;
324 int rc = 0;
325 uint64_t currentTime = 0;
326
327 rc = sd_event_now(event, CLOCK_MONOTONIC, &currentTime);
328 if (rc < 0)
329 {
330 log<level::ERR>("Failed to get the current timestamp",
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 get current timestamp");
333 }
334
335 // Create character accumulate timer
Vernon Mauery9e801a22018-10-12 13:20:49 -0700336 rc = sd_event_add_time(event, &accTimerSource, CLOCK_MONOTONIC,
337 currentTime + accumulateInterval.count(), 0,
338 charAccTimerHandler, static_cast<void*>(&instance));
Tom Josephe989c362017-03-14 17:16:50 +0530339 if (rc < 0)
340 {
341 log<level::ERR>("Failed to setup the accumulate timer",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700342 entry("RC=%d", rc));
Tom Josephe989c362017-03-14 17:16:50 +0530343 throw std::runtime_error("Failed to setup accumulate timer");
344 }
345
346 // Create retry interval timer and add to the event loop
Vernon Mauery9e801a22018-10-12 13:20:49 -0700347 rc = sd_event_add_time(event, &retryTimerSource, CLOCK_MONOTONIC,
348 currentTime + retryInterval.count(), 0,
349 retryTimerHandler, static_cast<void*>(&instance));
Tom Josephe989c362017-03-14 17:16:50 +0530350 if (rc < 0)
351 {
Vernon Mauery9e801a22018-10-12 13:20:49 -0700352 log<level::ERR>("Failed to setup the retry timer", entry("RC=%d", rc));
Tom Josephe989c362017-03-14 17:16:50 +0530353 throw std::runtime_error("Failed to setup retry timer");
354 }
355
356 // Enable the Character Accumulate Timer
357 rc = sd_event_source_set_enabled(accTimerSource, SD_EVENT_ONESHOT);
358 if (rc < 0)
359 {
360 log<level::ERR>("Failed to enable the accumulate timer",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700361 entry("RC=%d", rc));
Tom Josephe989c362017-03-14 17:16:50 +0530362 throw std::runtime_error("Failed to enable accumulate timer");
363 }
364
365 // Disable the Retry Interval Timer
366 rc = sd_event_source_set_enabled(retryTimerSource, SD_EVENT_OFF);
367 if (rc < 0)
368 {
369 log<level::ERR>("Failed to disable the retry timer",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700370 entry("RC=%d", rc));
Tom Josephe989c362017-03-14 17:16:50 +0530371 throw std::runtime_error("Failed to disable retry timer");
372 }
373
374 EventSource accEventSource(accTimerSource);
375 EventSource retryEventSource(retryTimerSource);
376 accTimerSource = nullptr;
377 retryTimerSource = nullptr;
378
379 TimerMap timer;
380 timer.emplace(Timers::ACCUMULATE, std::make_tuple(std::move(accEventSource),
381 accumulateInterval));
Vernon Mauery9e801a22018-10-12 13:20:49 -0700382 timer.emplace(Timers::RETRY,
383 std::make_tuple(std::move(retryEventSource), retryInterval));
Tom Josephe989c362017-03-14 17:16:50 +0530384 payloadInfo.emplace(instance, std::move(timer));
385}
386
Tom Josepha891eb72017-03-14 17:25:01 +0530387void EventLoop::stopSOLPayloadInstance(uint8_t payloadInst)
388{
389 auto iter = payloadInfo.find(payloadInst);
390 if (iter == payloadInfo.end())
391 {
392 log<level::ERR>("SOL Payload instance not found",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700393 entry("PAYLOADINST=%d", payloadInst));
Tom Josepha891eb72017-03-14 17:25:01 +0530394 throw std::runtime_error("SOL payload instance not found");
395 }
396
397 int rc = 0;
398
399 /* Destroy the character accumulate timer event source */
400 rc = sd_event_source_set_enabled(
Vernon Mauery9e801a22018-10-12 13:20:49 -0700401 (std::get<0>(iter->second.at(Timers::ACCUMULATE))).get(), SD_EVENT_OFF);
Tom Josepha891eb72017-03-14 17:25:01 +0530402 if (rc < 0)
403 {
404 log<level::ERR>("Failed to disable the character accumulate timer",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700405 entry("RC=%d", rc));
Tom Josepha891eb72017-03-14 17:25:01 +0530406 payloadInfo.erase(payloadInst);
407 throw std::runtime_error("Failed to disable accumulate timer");
408 }
409
410 /* Destroy the retry interval timer event source */
411 rc = sd_event_source_set_enabled(
Vernon Mauery9e801a22018-10-12 13:20:49 -0700412 (std::get<0>(iter->second.at(Timers::RETRY))).get(), SD_EVENT_OFF);
Tom Josepha891eb72017-03-14 17:25:01 +0530413 if (rc < 0)
414 {
415 log<level::ERR>("Failed to disable the retry timer",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700416 entry("RC=%d", rc));
Tom Josepha891eb72017-03-14 17:25:01 +0530417 payloadInfo.erase(payloadInst);
418 throw std::runtime_error("Failed to disable retry timer");
419 }
420
421 payloadInfo.erase(payloadInst);
422}
423
Vernon Mauery9e801a22018-10-12 13:20:49 -0700424void EventLoop::switchTimer(uint8_t payloadInst, Timers type, bool status)
Tom Joseph0a0d8f62017-03-14 17:04:58 +0530425{
426 auto iter = payloadInfo.find(payloadInst);
427 if (iter == payloadInfo.end())
428 {
429 log<level::ERR>("SOL Payload instance not found",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700430 entry("PAYLOADINST=%d", payloadInst));
Tom Joseph0a0d8f62017-03-14 17:04:58 +0530431 throw std::runtime_error("SOL Payload instance not found");
432 }
433
434 int rc = 0;
435 auto source = (std::get<0>(iter->second.at(type))).get();
436 auto interval = std::get<1>(iter->second.at(type));
437
438 // Turn OFF the timer
439 if (!status)
440 {
441 rc = sd_event_source_set_enabled(source, SD_EVENT_OFF);
442 if (rc < 0)
443 {
444 log<level::ERR>("Failed to disable the timer", entry("RC=%d", rc));
445 throw std::runtime_error("Failed to disable timer");
446 }
447 return;
448 }
449
450 // Turn ON the timer
451 uint64_t currentTime = 0;
452 rc = sd_event_now(event, CLOCK_MONOTONIC, &currentTime);
453 if (rc < 0)
454 {
455 log<level::ERR>("Failed to get the current timestamp",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700456 entry("RC=%d", rc));
Tom Joseph0a0d8f62017-03-14 17:04:58 +0530457 throw std::runtime_error("Failed to get current timestamp");
458 }
459
460 rc = sd_event_source_set_time(source, currentTime + interval.count());
461 if (rc < 0)
462 {
463 log<level::ERR>("sd_event_source_set_time function failed",
Vernon Mauery9e801a22018-10-12 13:20:49 -0700464 entry("RC=%d", rc));
Tom Joseph0a0d8f62017-03-14 17:04:58 +0530465 throw std::runtime_error("sd_event_source_set_time function failed");
466 }
467
468 rc = sd_event_source_set_enabled(source, SD_EVENT_ONESHOT);
469 if (rc < 0)
470 {
Vernon Mauery9e801a22018-10-12 13:20:49 -0700471 log<level::ERR>("Failed to enable the timer", entry("RC=%d", rc));
Tom Joseph0a0d8f62017-03-14 17:04:58 +0530472 throw std::runtime_error("Failed to enable timer");
473 }
474}
475
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530476} // namespace eventloop