blob: 16a5403d3142af6e70442082c75cea132db41a93 [file] [log] [blame]
Dave Cobbley2c15f0c2017-11-13 16:19:09 -08001#include <netinet/in.h>
Tom Joseph7fd26dd2017-03-14 15:26:26 +05302#include <sys/ioctl.h>
Dave Cobbley2c15f0c2017-11-13 16:19:09 -08003#include <sys/socket.h>
Tom Joseph7fd26dd2017-03-14 15:26:26 +05304#include <systemd/sd-daemon.h>
5#include <phosphor-logging/log.hpp>
6#include "main.hpp"
7#include "message_handler.hpp"
8#include "sd_event_loop.hpp"
9
10namespace eventloop
11{
12using namespace phosphor::logging;
13
14static int udp623Handler(sd_event_source* es, int fd, uint32_t revents,
15 void* userdata)
16{
17 std::shared_ptr<udpsocket::Channel> channelPtr;
18 struct timeval timeout;
19 timeout.tv_sec = SELECT_CALL_TIMEOUT;
20 timeout.tv_usec = 0;
21
22 try
23 {
24 channelPtr.reset(new udpsocket::Channel(fd, timeout));
25
26 // Initialize the Message Handler with the socket channel
27 message::Handler msgHandler(channelPtr);
28
29
30 std::unique_ptr<message::Message> inMessage;
31
32 // Read the incoming IPMI packet
33 inMessage = msgHandler.receive();
34 if (inMessage == nullptr)
35 {
36 return 0;
37 }
38
39 // Execute the Command
40 auto outMessage = msgHandler.executeCommand(*(inMessage.get()));
41 if (outMessage == nullptr)
42 {
43 return 0;
44 }
45
46 // Send the response IPMI Message
47 msgHandler.send(*(outMessage.get()));
48 }
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:",
Gunnar Mills576e2522017-10-25 09:28:06 -050068 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:",
91 entry("ERRNO=%d", errno));
92 }
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,
103 void *userdata)
104{
105 // The instance is hardcoded to 1, in the case of supporting multiple
106 // payload instances we would need to populate it from userdata
107 uint8_t instance = 1;
Tom Joseph73063142017-03-14 18:20:20 +0530108 int rc = 0;
Tom Josephe989c362017-03-14 17:16:50 +0530109 auto bufferSize = std::get<sol::Manager&>(singletonPool).dataBuffer.size();
110
111 try
112 {
113 if(bufferSize > 0)
114 {
Tom Joseph73063142017-03-14 18:20:20 +0530115 auto& context = std::get<sol::Manager&>(singletonPool).getContext
116 (instance);
117
118 rc = context.sendOutboundPayload();
119
120 if (rc == 0)
121 {
122 return 0;
123 }
Tom Josephe989c362017-03-14 17:16:50 +0530124 }
125
126 std::get<eventloop::EventLoop&>(singletonPool).switchTimer(
127 instance, Timers::ACCUMULATE, true);
128 }
129 catch (std::exception& e)
130 {
131 log<level::ERR>(e.what());
132 }
133
134 return 0;
135}
136
137static int retryTimerHandler(sd_event_source* s, uint64_t usec,
138 void *userdata)
139{
140 // The instance is hardcoded to 1, in the case of supporting multiple
141 // payload instances we would need to populate it from userdata
142 uint8_t instance = 1;
143
144 try
145 {
146 auto& context = std::get<sol::Manager&>(singletonPool).getContext
147 (instance);
148
149 if (context.retryCounter)
150 {
151 --context.retryCounter;
152 std::get<eventloop::EventLoop&>(singletonPool).switchTimer
153 (instance, Timers::RETRY, true);
Tom Joseph2fd466f2017-03-30 08:16:47 +0530154 context.resendPayload(sol::Context::noClear);
Tom Josephe989c362017-03-14 17:16:50 +0530155 }
156 else
157 {
158 context.retryCounter = context.maxRetryCount;
Tom Joseph2fd466f2017-03-30 08:16:47 +0530159 context.resendPayload(sol::Context::clear);
Tom Josephe989c362017-03-14 17:16:50 +0530160 std::get<eventloop::EventLoop&>(singletonPool).switchTimer
161 (instance, Timers::RETRY, false);
162 std::get<eventloop::EventLoop&>(singletonPool).switchTimer
163 (instance, Timers::ACCUMULATE, true);
164 }
165 }
166 catch (std::exception& e)
167 {
168 log<level::ERR>(e.what());
169 }
170
171 return 0;
172}
173
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530174int EventLoop::startEventLoop()
175{
176 int fd = -1;
177 int r = 0;
Dave Cobbley2c15f0c2017-11-13 16:19:09 -0800178 int listen_fd;
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530179 sigset_t ss;
180 sd_event_source* source = nullptr;
Marri Devender Rao3ecf0a12017-07-13 08:07:22 -0500181 auto bus = ipmid_get_sd_bus_connection();
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530182
183 r = sd_event_default(&event);
184 if (r < 0)
185 {
186 goto finish;
187 }
188
Marri Devender Rao3ecf0a12017-07-13 08:07:22 -0500189 // Attach the bus to sd_event to service user requests
190 r = sd_bus_attach_event(bus, event, SD_EVENT_PRIORITY_NORMAL);
191 if (r < 0)
192 {
193 goto finish;
194 }
195
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530196 if (sigemptyset(&ss) < 0 || sigaddset(&ss, SIGTERM) < 0 ||
197 sigaddset(&ss, SIGINT) < 0)
198 {
199 r = -errno;
200 goto finish;
201 }
202
203 /* Block SIGTERM first, so that the event loop can handle it */
204 if (sigprocmask(SIG_BLOCK, &ss, nullptr) < 0)
205 {
206 r = -errno;
207 goto finish;
208 }
209
210 /* Let's make use of the default handler and "floating" reference features
211 * of sd_event_add_signal() */
212 r = sd_event_add_signal(event, nullptr, SIGTERM, nullptr, nullptr);
213 if (r < 0)
214 {
215 goto finish;
216 }
217
218 r = sd_event_add_signal(event, nullptr, SIGINT, nullptr, nullptr);
219 if (r < 0)
220 {
221 goto finish;
222 }
223
Dave Cobbley2c15f0c2017-11-13 16:19:09 -0800224 //Create our own socket if SysD did not supply one.
225 listen_fd = sd_listen_fds(0);
226 if (listen_fd == 1)
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530227 {
Dave Cobbley2c15f0c2017-11-13 16:19:09 -0800228 fd = SD_LISTEN_FDS_START;
229 }
230 else if (listen_fd > 1)
231 {
232 log<level::ERR>("Too many file descriptors received");
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530233 goto finish;
234 }
Dave Cobbley2c15f0c2017-11-13 16:19:09 -0800235 else
236 {
237 struct sockaddr_in address;
238 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == 0)
239 {
240 r = -errno;
241 log<level::ERR>("Unable to manually open socket");
242 goto finish;
243 }
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530244
Dave Cobbley2c15f0c2017-11-13 16:19:09 -0800245 address.sin_family = AF_INET;
246 address.sin_addr.s_addr = INADDR_ANY;
247 address.sin_port = htons(IPMI_STD_PORT);
248
249 if (bind(fd, (struct sockaddr *)&address, sizeof(address)) < 0)
250 {
251 r = -errno;
252 log<level::ERR>("Unable to bind socket");
253 goto finish;
254 }
255 }
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530256
257 r = sd_event_add_io(event, &source, fd, EPOLLIN, udp623Handler, nullptr);
258 if (r < 0)
259 {
260 goto finish;
261 }
262
263 udpIPMI.reset(source);
264 source = nullptr;
265
266 r = sd_event_loop(event);
267
268finish:
269 event = sd_event_unref(event);
270
271 if (fd >= 0)
272 {
273 (void) close(fd);
274 }
275
276 if (r < 0)
277 {
278 log<level::ERR>("Event Loop Failure:",
279 entry("FAILURE=%s", strerror(-r)));
280 }
281
282 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
283}
284
Tom Joseph56f24e92017-03-14 16:06:31 +0530285void EventLoop::startHostConsole(const sol::CustomFD& fd)
286{
287 int rc = 0;
288
289 if((fd() == -1) || hostConsole.get())
290 {
291 throw std::runtime_error("Console descriptor already added");
292 }
293
294 sd_event_source* source = nullptr;
295
296 // Add the fd to the event loop for EPOLLIN
297 rc = sd_event_add_io(
298 event, &source, fd(), EPOLLIN, consoleInputHandler, nullptr);
299 if (rc < 0)
300 {
301 throw std::runtime_error("Failed to add socket descriptor");
302 }
303
304 hostConsole.reset(source);
305 source=nullptr;
306}
307
Tom Josephcfbd43f2017-03-14 16:14:03 +0530308void EventLoop::stopHostConsole()
309{
310 int rc = 0;
311
312 if (hostConsole.get())
313 {
314 // Disable the host console payload
315 rc = sd_event_source_set_enabled(hostConsole.get(), SD_EVENT_OFF);
316 if (rc < 0)
317 {
318 log<level::ERR>("Failed to disable the host console socket",
319 entry("RC=%d", rc));
Tom Josephcfbd43f2017-03-14 16:14:03 +0530320 }
321
322 hostConsole.reset();
323 }
324}
325
Tom Josephe989c362017-03-14 17:16:50 +0530326void EventLoop::startSOLPayloadInstance(uint8_t payloadInst,
327 IntervalType accumulateInterval,
328 IntervalType retryInterval)
329{
330 auto instance = payloadInst;
331 sd_event_source* accTimerSource = nullptr;
332 sd_event_source* retryTimerSource = nullptr;
333 int rc = 0;
334 uint64_t currentTime = 0;
335
336 rc = sd_event_now(event, CLOCK_MONOTONIC, &currentTime);
337 if (rc < 0)
338 {
339 log<level::ERR>("Failed to get the current timestamp",
340 entry("RC=%d", rc));
341 throw std::runtime_error("Failed to get current timestamp");
342 }
343
344 // Create character accumulate timer
345 rc = sd_event_add_time(event,
346 &accTimerSource,
347 CLOCK_MONOTONIC,
348 currentTime + accumulateInterval.count(),
349 0,
350 charAccTimerHandler,
351 static_cast<void *>(&instance));
352 if (rc < 0)
353 {
354 log<level::ERR>("Failed to setup the accumulate timer",
Gunnar Mills576e2522017-10-25 09:28:06 -0500355 entry("RC=%d", rc));
Tom Josephe989c362017-03-14 17:16:50 +0530356 throw std::runtime_error("Failed to setup accumulate timer");
357 }
358
359 // Create retry interval timer and add to the event loop
360 rc = sd_event_add_time(event,
361 &retryTimerSource,
362 CLOCK_MONOTONIC,
363 currentTime + retryInterval.count(),
364 0,
365 retryTimerHandler,
366 static_cast<void *>(&instance));
367 if (rc < 0)
368 {
369 log<level::ERR>("Failed to setup the retry timer",
Gunnar Mills576e2522017-10-25 09:28:06 -0500370 entry("RC=%d", rc));
Tom Josephe989c362017-03-14 17:16:50 +0530371 throw std::runtime_error("Failed to setup retry timer");
372 }
373
374 // Enable the Character Accumulate Timer
375 rc = sd_event_source_set_enabled(accTimerSource, SD_EVENT_ONESHOT);
376 if (rc < 0)
377 {
378 log<level::ERR>("Failed to enable the accumulate timer",
Gunnar Mills576e2522017-10-25 09:28:06 -0500379 entry("RC=%d", rc));
Tom Josephe989c362017-03-14 17:16:50 +0530380 throw std::runtime_error("Failed to enable accumulate timer");
381 }
382
383 // Disable the Retry Interval Timer
384 rc = sd_event_source_set_enabled(retryTimerSource, SD_EVENT_OFF);
385 if (rc < 0)
386 {
387 log<level::ERR>("Failed to disable the retry timer",
Gunnar Mills576e2522017-10-25 09:28:06 -0500388 entry("RC=%d", rc));
Tom Josephe989c362017-03-14 17:16:50 +0530389 throw std::runtime_error("Failed to disable retry timer");
390 }
391
392 EventSource accEventSource(accTimerSource);
393 EventSource retryEventSource(retryTimerSource);
394 accTimerSource = nullptr;
395 retryTimerSource = nullptr;
396
397 TimerMap timer;
398 timer.emplace(Timers::ACCUMULATE, std::make_tuple(std::move(accEventSource),
399 accumulateInterval));
400 timer.emplace(Timers::RETRY, std::make_tuple(std::move(retryEventSource),
401 retryInterval));
402 payloadInfo.emplace(instance, std::move(timer));
403}
404
Tom Josepha891eb72017-03-14 17:25:01 +0530405void EventLoop::stopSOLPayloadInstance(uint8_t payloadInst)
406{
407 auto iter = payloadInfo.find(payloadInst);
408 if (iter == payloadInfo.end())
409 {
410 log<level::ERR>("SOL Payload instance not found",
Gunnar Mills576e2522017-10-25 09:28:06 -0500411 entry("PAYLOADINST=%d", payloadInst));
Tom Josepha891eb72017-03-14 17:25:01 +0530412 throw std::runtime_error("SOL payload instance not found");
413 }
414
415 int rc = 0;
416
417 /* Destroy the character accumulate timer event source */
418 rc = sd_event_source_set_enabled(
419 (std::get<0>(iter->second.at(Timers::ACCUMULATE))).get(),
420 SD_EVENT_OFF);
421 if (rc < 0)
422 {
423 log<level::ERR>("Failed to disable the character accumulate timer",
424 entry("RC=%d", rc));
425 payloadInfo.erase(payloadInst);
426 throw std::runtime_error("Failed to disable accumulate timer");
427 }
428
429 /* Destroy the retry interval timer event source */
430 rc = sd_event_source_set_enabled(
431 (std::get<0>(iter->second.at(Timers::RETRY))).get(),
432 SD_EVENT_OFF);
433 if (rc < 0)
434 {
435 log<level::ERR>("Failed to disable the retry timer",
436 entry("RC=%d", rc));
437 payloadInfo.erase(payloadInst);
438 throw std::runtime_error("Failed to disable retry timer");
439 }
440
441 payloadInfo.erase(payloadInst);
442}
443
Tom Joseph0a0d8f62017-03-14 17:04:58 +0530444void EventLoop::switchTimer(uint8_t payloadInst,
445 Timers type,
446 bool status)
447{
448 auto iter = payloadInfo.find(payloadInst);
449 if (iter == payloadInfo.end())
450 {
451 log<level::ERR>("SOL Payload instance not found",
Gunnar Mills576e2522017-10-25 09:28:06 -0500452 entry("PAYLOADINST=%d", payloadInst));
Tom Joseph0a0d8f62017-03-14 17:04:58 +0530453 throw std::runtime_error("SOL Payload instance not found");
454 }
455
456 int rc = 0;
457 auto source = (std::get<0>(iter->second.at(type))).get();
458 auto interval = std::get<1>(iter->second.at(type));
459
460 // Turn OFF the timer
461 if (!status)
462 {
463 rc = sd_event_source_set_enabled(source, SD_EVENT_OFF);
464 if (rc < 0)
465 {
466 log<level::ERR>("Failed to disable the timer", entry("RC=%d", rc));
467 throw std::runtime_error("Failed to disable timer");
468 }
469 return;
470 }
471
472 // Turn ON the timer
473 uint64_t currentTime = 0;
474 rc = sd_event_now(event, CLOCK_MONOTONIC, &currentTime);
475 if (rc < 0)
476 {
477 log<level::ERR>("Failed to get the current timestamp",
478 entry("RC=%d", rc));
479 throw std::runtime_error("Failed to get current timestamp");
480 }
481
482 rc = sd_event_source_set_time(source, currentTime + interval.count());
483 if (rc < 0)
484 {
485 log<level::ERR>("sd_event_source_set_time function failed",
486 entry("RC=%d", rc));
487 throw std::runtime_error("sd_event_source_set_time function failed");
488 }
489
490 rc = sd_event_source_set_enabled(source, SD_EVENT_ONESHOT);
491 if (rc < 0)
492 {
493 log<level::ERR>("Failed to enable the timer", entry("RC=%d",rc));
494 throw std::runtime_error("Failed to enable timer");
495 }
496}
497
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530498} // namespace eventloop