blob: 3351da7eedf3275ccc6235f0cc5e84c59405fb3f [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
Ratan Gupta166c71a2018-03-23 23:05:17 +0530174int EventLoop::startEventLoop(sd_event* events)
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530175{
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
Ratan Gupta166c71a2018-03-23 23:05:17 +0530183 event = events;
Marri Devender Rao3ecf0a12017-07-13 08:07:22 -0500184 // Attach the bus to sd_event to service user requests
185 r = sd_bus_attach_event(bus, event, SD_EVENT_PRIORITY_NORMAL);
186 if (r < 0)
187 {
188 goto finish;
189 }
190
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530191 if (sigemptyset(&ss) < 0 || sigaddset(&ss, SIGTERM) < 0 ||
192 sigaddset(&ss, SIGINT) < 0)
193 {
194 r = -errno;
195 goto finish;
196 }
197
198 /* Block SIGTERM first, so that the event loop can handle it */
199 if (sigprocmask(SIG_BLOCK, &ss, nullptr) < 0)
200 {
201 r = -errno;
202 goto finish;
203 }
204
205 /* Let's make use of the default handler and "floating" reference features
206 * of sd_event_add_signal() */
207 r = sd_event_add_signal(event, nullptr, SIGTERM, nullptr, nullptr);
208 if (r < 0)
209 {
210 goto finish;
211 }
212
213 r = sd_event_add_signal(event, nullptr, SIGINT, nullptr, nullptr);
214 if (r < 0)
215 {
216 goto finish;
217 }
218
Dave Cobbley2c15f0c2017-11-13 16:19:09 -0800219 //Create our own socket if SysD did not supply one.
220 listen_fd = sd_listen_fds(0);
221 if (listen_fd == 1)
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530222 {
Dave Cobbley2c15f0c2017-11-13 16:19:09 -0800223 fd = SD_LISTEN_FDS_START;
224 }
225 else if (listen_fd > 1)
226 {
227 log<level::ERR>("Too many file descriptors received");
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530228 goto finish;
229 }
Dave Cobbley2c15f0c2017-11-13 16:19:09 -0800230 else
231 {
232 struct sockaddr_in address;
233 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == 0)
234 {
235 r = -errno;
236 log<level::ERR>("Unable to manually open socket");
237 goto finish;
238 }
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530239
Dave Cobbley2c15f0c2017-11-13 16:19:09 -0800240 address.sin_family = AF_INET;
241 address.sin_addr.s_addr = INADDR_ANY;
242 address.sin_port = htons(IPMI_STD_PORT);
243
244 if (bind(fd, (struct sockaddr *)&address, sizeof(address)) < 0)
245 {
246 r = -errno;
247 log<level::ERR>("Unable to bind socket");
248 goto finish;
249 }
250 }
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530251
252 r = sd_event_add_io(event, &source, fd, EPOLLIN, udp623Handler, nullptr);
253 if (r < 0)
254 {
255 goto finish;
256 }
257
258 udpIPMI.reset(source);
259 source = nullptr;
260
261 r = sd_event_loop(event);
262
263finish:
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530264
265 if (fd >= 0)
266 {
267 (void) close(fd);
268 }
269
270 if (r < 0)
271 {
272 log<level::ERR>("Event Loop Failure:",
273 entry("FAILURE=%s", strerror(-r)));
274 }
275
276 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
277}
278
Tom Joseph56f24e92017-03-14 16:06:31 +0530279void EventLoop::startHostConsole(const sol::CustomFD& fd)
280{
281 int rc = 0;
282
283 if((fd() == -1) || hostConsole.get())
284 {
285 throw std::runtime_error("Console descriptor already added");
286 }
287
288 sd_event_source* source = nullptr;
289
290 // Add the fd to the event loop for EPOLLIN
291 rc = sd_event_add_io(
292 event, &source, fd(), EPOLLIN, consoleInputHandler, nullptr);
293 if (rc < 0)
294 {
295 throw std::runtime_error("Failed to add socket descriptor");
296 }
297
298 hostConsole.reset(source);
299 source=nullptr;
300}
301
Tom Josephcfbd43f2017-03-14 16:14:03 +0530302void EventLoop::stopHostConsole()
303{
304 int rc = 0;
305
306 if (hostConsole.get())
307 {
308 // Disable the host console payload
309 rc = sd_event_source_set_enabled(hostConsole.get(), SD_EVENT_OFF);
310 if (rc < 0)
311 {
312 log<level::ERR>("Failed to disable the host console socket",
313 entry("RC=%d", rc));
Tom Josephcfbd43f2017-03-14 16:14:03 +0530314 }
315
316 hostConsole.reset();
317 }
318}
319
Tom Josephe989c362017-03-14 17:16:50 +0530320void EventLoop::startSOLPayloadInstance(uint8_t payloadInst,
321 IntervalType accumulateInterval,
322 IntervalType retryInterval)
323{
324 auto instance = payloadInst;
325 sd_event_source* accTimerSource = nullptr;
326 sd_event_source* retryTimerSource = nullptr;
327 int rc = 0;
328 uint64_t currentTime = 0;
329
330 rc = sd_event_now(event, CLOCK_MONOTONIC, &currentTime);
331 if (rc < 0)
332 {
333 log<level::ERR>("Failed to get the current timestamp",
334 entry("RC=%d", rc));
335 throw std::runtime_error("Failed to get current timestamp");
336 }
337
338 // Create character accumulate timer
339 rc = sd_event_add_time(event,
340 &accTimerSource,
341 CLOCK_MONOTONIC,
342 currentTime + accumulateInterval.count(),
343 0,
344 charAccTimerHandler,
345 static_cast<void *>(&instance));
346 if (rc < 0)
347 {
348 log<level::ERR>("Failed to setup the accumulate timer",
Gunnar Mills576e2522017-10-25 09:28:06 -0500349 entry("RC=%d", rc));
Tom Josephe989c362017-03-14 17:16:50 +0530350 throw std::runtime_error("Failed to setup accumulate timer");
351 }
352
353 // Create retry interval timer and add to the event loop
354 rc = sd_event_add_time(event,
355 &retryTimerSource,
356 CLOCK_MONOTONIC,
357 currentTime + retryInterval.count(),
358 0,
359 retryTimerHandler,
360 static_cast<void *>(&instance));
361 if (rc < 0)
362 {
363 log<level::ERR>("Failed to setup the retry timer",
Gunnar Mills576e2522017-10-25 09:28:06 -0500364 entry("RC=%d", rc));
Tom Josephe989c362017-03-14 17:16:50 +0530365 throw std::runtime_error("Failed to setup retry timer");
366 }
367
368 // Enable the Character Accumulate Timer
369 rc = sd_event_source_set_enabled(accTimerSource, SD_EVENT_ONESHOT);
370 if (rc < 0)
371 {
372 log<level::ERR>("Failed to enable the accumulate timer",
Gunnar Mills576e2522017-10-25 09:28:06 -0500373 entry("RC=%d", rc));
Tom Josephe989c362017-03-14 17:16:50 +0530374 throw std::runtime_error("Failed to enable accumulate timer");
375 }
376
377 // Disable the Retry Interval Timer
378 rc = sd_event_source_set_enabled(retryTimerSource, SD_EVENT_OFF);
379 if (rc < 0)
380 {
381 log<level::ERR>("Failed to disable the retry timer",
Gunnar Mills576e2522017-10-25 09:28:06 -0500382 entry("RC=%d", rc));
Tom Josephe989c362017-03-14 17:16:50 +0530383 throw std::runtime_error("Failed to disable retry timer");
384 }
385
386 EventSource accEventSource(accTimerSource);
387 EventSource retryEventSource(retryTimerSource);
388 accTimerSource = nullptr;
389 retryTimerSource = nullptr;
390
391 TimerMap timer;
392 timer.emplace(Timers::ACCUMULATE, std::make_tuple(std::move(accEventSource),
393 accumulateInterval));
394 timer.emplace(Timers::RETRY, std::make_tuple(std::move(retryEventSource),
395 retryInterval));
396 payloadInfo.emplace(instance, std::move(timer));
397}
398
Tom Josepha891eb72017-03-14 17:25:01 +0530399void EventLoop::stopSOLPayloadInstance(uint8_t payloadInst)
400{
401 auto iter = payloadInfo.find(payloadInst);
402 if (iter == payloadInfo.end())
403 {
404 log<level::ERR>("SOL Payload instance not found",
Gunnar Mills576e2522017-10-25 09:28:06 -0500405 entry("PAYLOADINST=%d", payloadInst));
Tom Josepha891eb72017-03-14 17:25:01 +0530406 throw std::runtime_error("SOL payload instance not found");
407 }
408
409 int rc = 0;
410
411 /* Destroy the character accumulate timer event source */
412 rc = sd_event_source_set_enabled(
413 (std::get<0>(iter->second.at(Timers::ACCUMULATE))).get(),
414 SD_EVENT_OFF);
415 if (rc < 0)
416 {
417 log<level::ERR>("Failed to disable the character accumulate timer",
418 entry("RC=%d", rc));
419 payloadInfo.erase(payloadInst);
420 throw std::runtime_error("Failed to disable accumulate timer");
421 }
422
423 /* Destroy the retry interval timer event source */
424 rc = sd_event_source_set_enabled(
425 (std::get<0>(iter->second.at(Timers::RETRY))).get(),
426 SD_EVENT_OFF);
427 if (rc < 0)
428 {
429 log<level::ERR>("Failed to disable the retry timer",
430 entry("RC=%d", rc));
431 payloadInfo.erase(payloadInst);
432 throw std::runtime_error("Failed to disable retry timer");
433 }
434
435 payloadInfo.erase(payloadInst);
436}
437
Tom Joseph0a0d8f62017-03-14 17:04:58 +0530438void EventLoop::switchTimer(uint8_t payloadInst,
439 Timers type,
440 bool status)
441{
442 auto iter = payloadInfo.find(payloadInst);
443 if (iter == payloadInfo.end())
444 {
445 log<level::ERR>("SOL Payload instance not found",
Gunnar Mills576e2522017-10-25 09:28:06 -0500446 entry("PAYLOADINST=%d", payloadInst));
Tom Joseph0a0d8f62017-03-14 17:04:58 +0530447 throw std::runtime_error("SOL Payload instance not found");
448 }
449
450 int rc = 0;
451 auto source = (std::get<0>(iter->second.at(type))).get();
452 auto interval = std::get<1>(iter->second.at(type));
453
454 // Turn OFF the timer
455 if (!status)
456 {
457 rc = sd_event_source_set_enabled(source, SD_EVENT_OFF);
458 if (rc < 0)
459 {
460 log<level::ERR>("Failed to disable the timer", entry("RC=%d", rc));
461 throw std::runtime_error("Failed to disable timer");
462 }
463 return;
464 }
465
466 // Turn ON the timer
467 uint64_t currentTime = 0;
468 rc = sd_event_now(event, CLOCK_MONOTONIC, &currentTime);
469 if (rc < 0)
470 {
471 log<level::ERR>("Failed to get the current timestamp",
472 entry("RC=%d", rc));
473 throw std::runtime_error("Failed to get current timestamp");
474 }
475
476 rc = sd_event_source_set_time(source, currentTime + interval.count());
477 if (rc < 0)
478 {
479 log<level::ERR>("sd_event_source_set_time function failed",
480 entry("RC=%d", rc));
481 throw std::runtime_error("sd_event_source_set_time function failed");
482 }
483
484 rc = sd_event_source_set_enabled(source, SD_EVENT_ONESHOT);
485 if (rc < 0)
486 {
487 log<level::ERR>("Failed to enable the timer", entry("RC=%d",rc));
488 throw std::runtime_error("Failed to enable timer");
489 }
490}
491
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530492} // namespace eventloop