blob: ed3e5476fe1ec5a390f7d7a4390928cb5147a90c [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:",
66 entry("errno = %d", errno));
67 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;
178
179 r = sd_event_default(&event);
180 if (r < 0)
181 {
182 goto finish;
183 }
184
185 if (sigemptyset(&ss) < 0 || sigaddset(&ss, SIGTERM) < 0 ||
186 sigaddset(&ss, SIGINT) < 0)
187 {
188 r = -errno;
189 goto finish;
190 }
191
192 /* Block SIGTERM first, so that the event loop can handle it */
193 if (sigprocmask(SIG_BLOCK, &ss, nullptr) < 0)
194 {
195 r = -errno;
196 goto finish;
197 }
198
199 /* Let's make use of the default handler and "floating" reference features
200 * of sd_event_add_signal() */
201 r = sd_event_add_signal(event, nullptr, SIGTERM, nullptr, nullptr);
202 if (r < 0)
203 {
204 goto finish;
205 }
206
207 r = sd_event_add_signal(event, nullptr, SIGINT, nullptr, nullptr);
208 if (r < 0)
209 {
210 goto finish;
211 }
212
213 if (sd_listen_fds(0) != 1)
214 {
215 log<level::ERR>("No or too many file descriptors received");
216 goto finish;
217 }
218
219 fd = SD_LISTEN_FDS_START;
220
221 r = sd_event_add_io(event, &source, fd, EPOLLIN, udp623Handler, nullptr);
222 if (r < 0)
223 {
224 goto finish;
225 }
226
227 udpIPMI.reset(source);
228 source = nullptr;
229
230 r = sd_event_loop(event);
231
232finish:
233 event = sd_event_unref(event);
234
235 if (fd >= 0)
236 {
237 (void) close(fd);
238 }
239
240 if (r < 0)
241 {
242 log<level::ERR>("Event Loop Failure:",
243 entry("FAILURE=%s", strerror(-r)));
244 }
245
246 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
247}
248
Tom Joseph56f24e92017-03-14 16:06:31 +0530249void EventLoop::startHostConsole(const sol::CustomFD& fd)
250{
251 int rc = 0;
252
253 if((fd() == -1) || hostConsole.get())
254 {
255 throw std::runtime_error("Console descriptor already added");
256 }
257
258 sd_event_source* source = nullptr;
259
260 // Add the fd to the event loop for EPOLLIN
261 rc = sd_event_add_io(
262 event, &source, fd(), EPOLLIN, consoleInputHandler, nullptr);
263 if (rc < 0)
264 {
265 throw std::runtime_error("Failed to add socket descriptor");
266 }
267
268 hostConsole.reset(source);
269 source=nullptr;
270}
271
Tom Josephcfbd43f2017-03-14 16:14:03 +0530272void EventLoop::stopHostConsole()
273{
274 int rc = 0;
275
276 if (hostConsole.get())
277 {
278 // Disable the host console payload
279 rc = sd_event_source_set_enabled(hostConsole.get(), SD_EVENT_OFF);
280 if (rc < 0)
281 {
282 log<level::ERR>("Failed to disable the host console socket",
283 entry("RC=%d", rc));
Tom Josephcfbd43f2017-03-14 16:14:03 +0530284 }
285
286 hostConsole.reset();
287 }
288}
289
Tom Josephe989c362017-03-14 17:16:50 +0530290void EventLoop::startSOLPayloadInstance(uint8_t payloadInst,
291 IntervalType accumulateInterval,
292 IntervalType retryInterval)
293{
294 auto instance = payloadInst;
295 sd_event_source* accTimerSource = nullptr;
296 sd_event_source* retryTimerSource = nullptr;
297 int rc = 0;
298 uint64_t currentTime = 0;
299
300 rc = sd_event_now(event, CLOCK_MONOTONIC, &currentTime);
301 if (rc < 0)
302 {
303 log<level::ERR>("Failed to get the current timestamp",
304 entry("RC=%d", rc));
305 throw std::runtime_error("Failed to get current timestamp");
306 }
307
308 // Create character accumulate timer
309 rc = sd_event_add_time(event,
310 &accTimerSource,
311 CLOCK_MONOTONIC,
312 currentTime + accumulateInterval.count(),
313 0,
314 charAccTimerHandler,
315 static_cast<void *>(&instance));
316 if (rc < 0)
317 {
318 log<level::ERR>("Failed to setup the accumulate timer",
319 entry("RC = %d", rc));
320 throw std::runtime_error("Failed to setup accumulate timer");
321 }
322
323 // Create retry interval timer and add to the event loop
324 rc = sd_event_add_time(event,
325 &retryTimerSource,
326 CLOCK_MONOTONIC,
327 currentTime + retryInterval.count(),
328 0,
329 retryTimerHandler,
330 static_cast<void *>(&instance));
331 if (rc < 0)
332 {
333 log<level::ERR>("Failed to setup the retry timer",
334 entry("RC = %d", rc));
335 throw std::runtime_error("Failed to setup retry timer");
336 }
337
338 // Enable the Character Accumulate Timer
339 rc = sd_event_source_set_enabled(accTimerSource, SD_EVENT_ONESHOT);
340 if (rc < 0)
341 {
342 log<level::ERR>("Failed to enable the accumulate timer",
343 entry("rc = %d", rc));
344 throw std::runtime_error("Failed to enable accumulate timer");
345 }
346
347 // Disable the Retry Interval Timer
348 rc = sd_event_source_set_enabled(retryTimerSource, SD_EVENT_OFF);
349 if (rc < 0)
350 {
351 log<level::ERR>("Failed to disable the retry timer",
352 entry("RC = %d", rc));
353 throw std::runtime_error("Failed to disable retry timer");
354 }
355
356 EventSource accEventSource(accTimerSource);
357 EventSource retryEventSource(retryTimerSource);
358 accTimerSource = nullptr;
359 retryTimerSource = nullptr;
360
361 TimerMap timer;
362 timer.emplace(Timers::ACCUMULATE, std::make_tuple(std::move(accEventSource),
363 accumulateInterval));
364 timer.emplace(Timers::RETRY, std::make_tuple(std::move(retryEventSource),
365 retryInterval));
366 payloadInfo.emplace(instance, std::move(timer));
367}
368
Tom Josepha891eb72017-03-14 17:25:01 +0530369void EventLoop::stopSOLPayloadInstance(uint8_t payloadInst)
370{
371 auto iter = payloadInfo.find(payloadInst);
372 if (iter == payloadInfo.end())
373 {
374 log<level::ERR>("SOL Payload instance not found",
375 entry("payloadInst=%d", payloadInst));
376 throw std::runtime_error("SOL payload instance not found");
377 }
378
379 int rc = 0;
380
381 /* Destroy the character accumulate timer event source */
382 rc = sd_event_source_set_enabled(
383 (std::get<0>(iter->second.at(Timers::ACCUMULATE))).get(),
384 SD_EVENT_OFF);
385 if (rc < 0)
386 {
387 log<level::ERR>("Failed to disable the character accumulate timer",
388 entry("RC=%d", rc));
389 payloadInfo.erase(payloadInst);
390 throw std::runtime_error("Failed to disable accumulate timer");
391 }
392
393 /* Destroy the retry interval timer event source */
394 rc = sd_event_source_set_enabled(
395 (std::get<0>(iter->second.at(Timers::RETRY))).get(),
396 SD_EVENT_OFF);
397 if (rc < 0)
398 {
399 log<level::ERR>("Failed to disable the retry timer",
400 entry("RC=%d", rc));
401 payloadInfo.erase(payloadInst);
402 throw std::runtime_error("Failed to disable retry timer");
403 }
404
405 payloadInfo.erase(payloadInst);
406}
407
Tom Joseph0a0d8f62017-03-14 17:04:58 +0530408void EventLoop::switchTimer(uint8_t payloadInst,
409 Timers type,
410 bool status)
411{
412 auto iter = payloadInfo.find(payloadInst);
413 if (iter == payloadInfo.end())
414 {
415 log<level::ERR>("SOL Payload instance not found",
416 entry("payloadInst=%d", payloadInst));
417 throw std::runtime_error("SOL Payload instance not found");
418 }
419
420 int rc = 0;
421 auto source = (std::get<0>(iter->second.at(type))).get();
422 auto interval = std::get<1>(iter->second.at(type));
423
424 // Turn OFF the timer
425 if (!status)
426 {
427 rc = sd_event_source_set_enabled(source, SD_EVENT_OFF);
428 if (rc < 0)
429 {
430 log<level::ERR>("Failed to disable the timer", entry("RC=%d", rc));
431 throw std::runtime_error("Failed to disable timer");
432 }
433 return;
434 }
435
436 // Turn ON the timer
437 uint64_t currentTime = 0;
438 rc = sd_event_now(event, CLOCK_MONOTONIC, &currentTime);
439 if (rc < 0)
440 {
441 log<level::ERR>("Failed to get the current timestamp",
442 entry("RC=%d", rc));
443 throw std::runtime_error("Failed to get current timestamp");
444 }
445
446 rc = sd_event_source_set_time(source, currentTime + interval.count());
447 if (rc < 0)
448 {
449 log<level::ERR>("sd_event_source_set_time function failed",
450 entry("RC=%d", rc));
451 throw std::runtime_error("sd_event_source_set_time function failed");
452 }
453
454 rc = sd_event_source_set_enabled(source, SD_EVENT_ONESHOT);
455 if (rc < 0)
456 {
457 log<level::ERR>("Failed to enable the timer", entry("RC=%d",rc));
458 throw std::runtime_error("Failed to enable timer");
459 }
460}
461
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530462} // namespace eventloop