blob: 0d8b3231a4a4cad86406bffb4d59ccacf7275d6c [file] [log] [blame]
Tom Joseph7fd26dd2017-03-14 15:26:26 +05301#include <sys/ioctl.h>
2#include <systemd/sd-daemon.h>
3#include <phosphor-logging/log.hpp>
4#include "main.hpp"
5#include "message_handler.hpp"
6#include "sd_event_loop.hpp"
7
8namespace eventloop
9{
10using namespace phosphor::logging;
11
12static int udp623Handler(sd_event_source* es, int fd, uint32_t revents,
13 void* userdata)
14{
15 std::shared_ptr<udpsocket::Channel> channelPtr;
16 struct timeval timeout;
17 timeout.tv_sec = SELECT_CALL_TIMEOUT;
18 timeout.tv_usec = 0;
19
20 try
21 {
22 channelPtr.reset(new udpsocket::Channel(fd, timeout));
23
24 // Initialize the Message Handler with the socket channel
25 message::Handler msgHandler(channelPtr);
26
27
28 std::unique_ptr<message::Message> inMessage;
29
30 // Read the incoming IPMI packet
31 inMessage = msgHandler.receive();
32 if (inMessage == nullptr)
33 {
34 return 0;
35 }
36
37 // Execute the Command
38 auto outMessage = msgHandler.executeCommand(*(inMessage.get()));
39 if (outMessage == nullptr)
40 {
41 return 0;
42 }
43
44 // Send the response IPMI Message
45 msgHandler.send(*(outMessage.get()));
46 }
47 catch (std::exception& e)
48 {
49 log<level::ERR>("Executing the IPMI message failed");
50 log<level::ERR>(e.what());
51 }
52
53 return 0;
54}
55
Tom Joseph56f24e92017-03-14 16:06:31 +053056static int consoleInputHandler(sd_event_source* es, int fd, uint32_t revents,
57 void* userdata)
58{
59 try
60 {
61 int readSize = 0;
62
63 if (ioctl(fd, FIONREAD, &readSize) < 0)
64 {
65 log<level::ERR>("ioctl failed for FIONREAD:",
Gunnar Mills576e2522017-10-25 09:28:06 -050066 entry("ERRNO=%d", errno));
Tom Joseph56f24e92017-03-14 16:06:31 +053067 return 0;
68 }
69
70 std::vector<uint8_t> buffer(readSize);
71 auto bufferSize = buffer.size();
72 ssize_t readDataLen = 0;
73
74 readDataLen = read(fd, buffer.data(), bufferSize);
75
76 // Update the Console buffer with data read from the socket
77 if (readDataLen > 0)
78 {
79 buffer.resize(readDataLen);
80 std::get<sol::Manager&>(singletonPool).dataBuffer.write(buffer);
81 }
82 else if (readDataLen == 0)
83 {
84 log<level::ERR>("Connection Closed for host console socket");
85 }
86 else if (readDataLen < 0) // Error
87 {
88 log<level::ERR>("Reading from host console socket failed:",
89 entry("ERRNO=%d", errno));
90 }
91 }
92 catch (std::exception& e)
93 {
94 log<level::ERR>(e.what());
95 }
96
97 return 0;
98}
99
Tom Josephe989c362017-03-14 17:16:50 +0530100static int charAccTimerHandler(sd_event_source* s, uint64_t usec,
101 void *userdata)
102{
103 // The instance is hardcoded to 1, in the case of supporting multiple
104 // payload instances we would need to populate it from userdata
105 uint8_t instance = 1;
Tom Joseph73063142017-03-14 18:20:20 +0530106 int rc = 0;
Tom Josephe989c362017-03-14 17:16:50 +0530107 auto bufferSize = std::get<sol::Manager&>(singletonPool).dataBuffer.size();
108
109 try
110 {
111 if(bufferSize > 0)
112 {
Tom Joseph73063142017-03-14 18:20:20 +0530113 auto& context = std::get<sol::Manager&>(singletonPool).getContext
114 (instance);
115
116 rc = context.sendOutboundPayload();
117
118 if (rc == 0)
119 {
120 return 0;
121 }
Tom Josephe989c362017-03-14 17:16:50 +0530122 }
123
124 std::get<eventloop::EventLoop&>(singletonPool).switchTimer(
125 instance, Timers::ACCUMULATE, true);
126 }
127 catch (std::exception& e)
128 {
129 log<level::ERR>(e.what());
130 }
131
132 return 0;
133}
134
135static int retryTimerHandler(sd_event_source* s, uint64_t usec,
136 void *userdata)
137{
138 // The instance is hardcoded to 1, in the case of supporting multiple
139 // payload instances we would need to populate it from userdata
140 uint8_t instance = 1;
141
142 try
143 {
144 auto& context = std::get<sol::Manager&>(singletonPool).getContext
145 (instance);
146
147 if (context.retryCounter)
148 {
149 --context.retryCounter;
150 std::get<eventloop::EventLoop&>(singletonPool).switchTimer
151 (instance, Timers::RETRY, true);
Tom Joseph2fd466f2017-03-30 08:16:47 +0530152 context.resendPayload(sol::Context::noClear);
Tom Josephe989c362017-03-14 17:16:50 +0530153 }
154 else
155 {
156 context.retryCounter = context.maxRetryCount;
Tom Joseph2fd466f2017-03-30 08:16:47 +0530157 context.resendPayload(sol::Context::clear);
Tom Josephe989c362017-03-14 17:16:50 +0530158 std::get<eventloop::EventLoop&>(singletonPool).switchTimer
159 (instance, Timers::RETRY, false);
160 std::get<eventloop::EventLoop&>(singletonPool).switchTimer
161 (instance, Timers::ACCUMULATE, true);
162 }
163 }
164 catch (std::exception& e)
165 {
166 log<level::ERR>(e.what());
167 }
168
169 return 0;
170}
171
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530172int EventLoop::startEventLoop()
173{
174 int fd = -1;
175 int r = 0;
176 sigset_t ss;
177 sd_event_source* source = nullptr;
Marri Devender Rao3ecf0a12017-07-13 08:07:22 -0500178 auto bus = ipmid_get_sd_bus_connection();
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530179
180 r = sd_event_default(&event);
181 if (r < 0)
182 {
183 goto finish;
184 }
185
Marri Devender Rao3ecf0a12017-07-13 08:07:22 -0500186 // Attach the bus to sd_event to service user requests
187 r = sd_bus_attach_event(bus, event, SD_EVENT_PRIORITY_NORMAL);
188 if (r < 0)
189 {
190 goto finish;
191 }
192
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530193 if (sigemptyset(&ss) < 0 || sigaddset(&ss, SIGTERM) < 0 ||
194 sigaddset(&ss, SIGINT) < 0)
195 {
196 r = -errno;
197 goto finish;
198 }
199
200 /* Block SIGTERM first, so that the event loop can handle it */
201 if (sigprocmask(SIG_BLOCK, &ss, nullptr) < 0)
202 {
203 r = -errno;
204 goto finish;
205 }
206
207 /* Let's make use of the default handler and "floating" reference features
208 * of sd_event_add_signal() */
209 r = sd_event_add_signal(event, nullptr, SIGTERM, nullptr, nullptr);
210 if (r < 0)
211 {
212 goto finish;
213 }
214
215 r = sd_event_add_signal(event, nullptr, SIGINT, nullptr, nullptr);
216 if (r < 0)
217 {
218 goto finish;
219 }
220
221 if (sd_listen_fds(0) != 1)
222 {
223 log<level::ERR>("No or too many file descriptors received");
224 goto finish;
225 }
226
227 fd = SD_LISTEN_FDS_START;
228
229 r = sd_event_add_io(event, &source, fd, EPOLLIN, udp623Handler, nullptr);
230 if (r < 0)
231 {
232 goto finish;
233 }
234
235 udpIPMI.reset(source);
236 source = nullptr;
237
238 r = sd_event_loop(event);
239
240finish:
241 event = sd_event_unref(event);
242
243 if (fd >= 0)
244 {
245 (void) close(fd);
246 }
247
248 if (r < 0)
249 {
250 log<level::ERR>("Event Loop Failure:",
251 entry("FAILURE=%s", strerror(-r)));
252 }
253
254 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
255}
256
Tom Joseph56f24e92017-03-14 16:06:31 +0530257void EventLoop::startHostConsole(const sol::CustomFD& fd)
258{
259 int rc = 0;
260
261 if((fd() == -1) || hostConsole.get())
262 {
263 throw std::runtime_error("Console descriptor already added");
264 }
265
266 sd_event_source* source = nullptr;
267
268 // Add the fd to the event loop for EPOLLIN
269 rc = sd_event_add_io(
270 event, &source, fd(), EPOLLIN, consoleInputHandler, nullptr);
271 if (rc < 0)
272 {
273 throw std::runtime_error("Failed to add socket descriptor");
274 }
275
276 hostConsole.reset(source);
277 source=nullptr;
278}
279
Tom Josephcfbd43f2017-03-14 16:14:03 +0530280void EventLoop::stopHostConsole()
281{
282 int rc = 0;
283
284 if (hostConsole.get())
285 {
286 // Disable the host console payload
287 rc = sd_event_source_set_enabled(hostConsole.get(), SD_EVENT_OFF);
288 if (rc < 0)
289 {
290 log<level::ERR>("Failed to disable the host console socket",
291 entry("RC=%d", rc));
Tom Josephcfbd43f2017-03-14 16:14:03 +0530292 }
293
294 hostConsole.reset();
295 }
296}
297
Tom Josephe989c362017-03-14 17:16:50 +0530298void EventLoop::startSOLPayloadInstance(uint8_t payloadInst,
299 IntervalType accumulateInterval,
300 IntervalType retryInterval)
301{
302 auto instance = payloadInst;
303 sd_event_source* accTimerSource = nullptr;
304 sd_event_source* retryTimerSource = nullptr;
305 int rc = 0;
306 uint64_t currentTime = 0;
307
308 rc = sd_event_now(event, CLOCK_MONOTONIC, &currentTime);
309 if (rc < 0)
310 {
311 log<level::ERR>("Failed to get the current timestamp",
312 entry("RC=%d", rc));
313 throw std::runtime_error("Failed to get current timestamp");
314 }
315
316 // Create character accumulate timer
317 rc = sd_event_add_time(event,
318 &accTimerSource,
319 CLOCK_MONOTONIC,
320 currentTime + accumulateInterval.count(),
321 0,
322 charAccTimerHandler,
323 static_cast<void *>(&instance));
324 if (rc < 0)
325 {
326 log<level::ERR>("Failed to setup the accumulate timer",
Gunnar Mills576e2522017-10-25 09:28:06 -0500327 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
332 rc = sd_event_add_time(event,
333 &retryTimerSource,
334 CLOCK_MONOTONIC,
335 currentTime + retryInterval.count(),
336 0,
337 retryTimerHandler,
338 static_cast<void *>(&instance));
339 if (rc < 0)
340 {
341 log<level::ERR>("Failed to setup the retry timer",
Gunnar Mills576e2522017-10-25 09:28:06 -0500342 entry("RC=%d", rc));
Tom Josephe989c362017-03-14 17:16:50 +0530343 throw std::runtime_error("Failed to setup retry timer");
344 }
345
346 // Enable the Character Accumulate Timer
347 rc = sd_event_source_set_enabled(accTimerSource, SD_EVENT_ONESHOT);
348 if (rc < 0)
349 {
350 log<level::ERR>("Failed to enable the accumulate timer",
Gunnar Mills576e2522017-10-25 09:28:06 -0500351 entry("RC=%d", rc));
Tom Josephe989c362017-03-14 17:16:50 +0530352 throw std::runtime_error("Failed to enable accumulate timer");
353 }
354
355 // Disable the Retry Interval Timer
356 rc = sd_event_source_set_enabled(retryTimerSource, SD_EVENT_OFF);
357 if (rc < 0)
358 {
359 log<level::ERR>("Failed to disable the retry timer",
Gunnar Mills576e2522017-10-25 09:28:06 -0500360 entry("RC=%d", rc));
Tom Josephe989c362017-03-14 17:16:50 +0530361 throw std::runtime_error("Failed to disable retry timer");
362 }
363
364 EventSource accEventSource(accTimerSource);
365 EventSource retryEventSource(retryTimerSource);
366 accTimerSource = nullptr;
367 retryTimerSource = nullptr;
368
369 TimerMap timer;
370 timer.emplace(Timers::ACCUMULATE, std::make_tuple(std::move(accEventSource),
371 accumulateInterval));
372 timer.emplace(Timers::RETRY, std::make_tuple(std::move(retryEventSource),
373 retryInterval));
374 payloadInfo.emplace(instance, std::move(timer));
375}
376
Tom Josepha891eb72017-03-14 17:25:01 +0530377void EventLoop::stopSOLPayloadInstance(uint8_t payloadInst)
378{
379 auto iter = payloadInfo.find(payloadInst);
380 if (iter == payloadInfo.end())
381 {
382 log<level::ERR>("SOL Payload instance not found",
Gunnar Mills576e2522017-10-25 09:28:06 -0500383 entry("PAYLOADINST=%d", payloadInst));
Tom Josepha891eb72017-03-14 17:25:01 +0530384 throw std::runtime_error("SOL payload instance not found");
385 }
386
387 int rc = 0;
388
389 /* Destroy the character accumulate timer event source */
390 rc = sd_event_source_set_enabled(
391 (std::get<0>(iter->second.at(Timers::ACCUMULATE))).get(),
392 SD_EVENT_OFF);
393 if (rc < 0)
394 {
395 log<level::ERR>("Failed to disable the character accumulate timer",
396 entry("RC=%d", rc));
397 payloadInfo.erase(payloadInst);
398 throw std::runtime_error("Failed to disable accumulate timer");
399 }
400
401 /* Destroy the retry interval timer event source */
402 rc = sd_event_source_set_enabled(
403 (std::get<0>(iter->second.at(Timers::RETRY))).get(),
404 SD_EVENT_OFF);
405 if (rc < 0)
406 {
407 log<level::ERR>("Failed to disable the retry timer",
408 entry("RC=%d", rc));
409 payloadInfo.erase(payloadInst);
410 throw std::runtime_error("Failed to disable retry timer");
411 }
412
413 payloadInfo.erase(payloadInst);
414}
415
Tom Joseph0a0d8f62017-03-14 17:04:58 +0530416void EventLoop::switchTimer(uint8_t payloadInst,
417 Timers type,
418 bool status)
419{
420 auto iter = payloadInfo.find(payloadInst);
421 if (iter == payloadInfo.end())
422 {
423 log<level::ERR>("SOL Payload instance not found",
Gunnar Mills576e2522017-10-25 09:28:06 -0500424 entry("PAYLOADINST=%d", payloadInst));
Tom Joseph0a0d8f62017-03-14 17:04:58 +0530425 throw std::runtime_error("SOL Payload instance not found");
426 }
427
428 int rc = 0;
429 auto source = (std::get<0>(iter->second.at(type))).get();
430 auto interval = std::get<1>(iter->second.at(type));
431
432 // Turn OFF the timer
433 if (!status)
434 {
435 rc = sd_event_source_set_enabled(source, SD_EVENT_OFF);
436 if (rc < 0)
437 {
438 log<level::ERR>("Failed to disable the timer", entry("RC=%d", rc));
439 throw std::runtime_error("Failed to disable timer");
440 }
441 return;
442 }
443
444 // Turn ON the timer
445 uint64_t currentTime = 0;
446 rc = sd_event_now(event, CLOCK_MONOTONIC, &currentTime);
447 if (rc < 0)
448 {
449 log<level::ERR>("Failed to get the current timestamp",
450 entry("RC=%d", rc));
451 throw std::runtime_error("Failed to get current timestamp");
452 }
453
454 rc = sd_event_source_set_time(source, currentTime + interval.count());
455 if (rc < 0)
456 {
457 log<level::ERR>("sd_event_source_set_time function failed",
458 entry("RC=%d", rc));
459 throw std::runtime_error("sd_event_source_set_time function failed");
460 }
461
462 rc = sd_event_source_set_enabled(source, SD_EVENT_ONESHOT);
463 if (rc < 0)
464 {
465 log<level::ERR>("Failed to enable the timer", entry("RC=%d",rc));
466 throw std::runtime_error("Failed to enable timer");
467 }
468}
469
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530470} // namespace eventloop