blob: af1a5ee00f432fbf033dd476b96f3d961aa770d4 [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));
284 hostConsole.reset();
285 throw std::runtime_error("Failed to disable socket descriptor");
286 }
287
288 hostConsole.reset();
289 }
290}
291
Tom Josephe989c362017-03-14 17:16:50 +0530292void EventLoop::startSOLPayloadInstance(uint8_t payloadInst,
293 IntervalType accumulateInterval,
294 IntervalType retryInterval)
295{
296 auto instance = payloadInst;
297 sd_event_source* accTimerSource = nullptr;
298 sd_event_source* retryTimerSource = nullptr;
299 int rc = 0;
300 uint64_t currentTime = 0;
301
302 rc = sd_event_now(event, CLOCK_MONOTONIC, &currentTime);
303 if (rc < 0)
304 {
305 log<level::ERR>("Failed to get the current timestamp",
306 entry("RC=%d", rc));
307 throw std::runtime_error("Failed to get current timestamp");
308 }
309
310 // Create character accumulate timer
311 rc = sd_event_add_time(event,
312 &accTimerSource,
313 CLOCK_MONOTONIC,
314 currentTime + accumulateInterval.count(),
315 0,
316 charAccTimerHandler,
317 static_cast<void *>(&instance));
318 if (rc < 0)
319 {
320 log<level::ERR>("Failed to setup the accumulate timer",
321 entry("RC = %d", rc));
322 throw std::runtime_error("Failed to setup accumulate timer");
323 }
324
325 // Create retry interval timer and add to the event loop
326 rc = sd_event_add_time(event,
327 &retryTimerSource,
328 CLOCK_MONOTONIC,
329 currentTime + retryInterval.count(),
330 0,
331 retryTimerHandler,
332 static_cast<void *>(&instance));
333 if (rc < 0)
334 {
335 log<level::ERR>("Failed to setup the retry timer",
336 entry("RC = %d", rc));
337 throw std::runtime_error("Failed to setup retry timer");
338 }
339
340 // Enable the Character Accumulate Timer
341 rc = sd_event_source_set_enabled(accTimerSource, SD_EVENT_ONESHOT);
342 if (rc < 0)
343 {
344 log<level::ERR>("Failed to enable the accumulate timer",
345 entry("rc = %d", rc));
346 throw std::runtime_error("Failed to enable accumulate timer");
347 }
348
349 // Disable the Retry Interval Timer
350 rc = sd_event_source_set_enabled(retryTimerSource, SD_EVENT_OFF);
351 if (rc < 0)
352 {
353 log<level::ERR>("Failed to disable the retry timer",
354 entry("RC = %d", rc));
355 throw std::runtime_error("Failed to disable retry timer");
356 }
357
358 EventSource accEventSource(accTimerSource);
359 EventSource retryEventSource(retryTimerSource);
360 accTimerSource = nullptr;
361 retryTimerSource = nullptr;
362
363 TimerMap timer;
364 timer.emplace(Timers::ACCUMULATE, std::make_tuple(std::move(accEventSource),
365 accumulateInterval));
366 timer.emplace(Timers::RETRY, std::make_tuple(std::move(retryEventSource),
367 retryInterval));
368 payloadInfo.emplace(instance, std::move(timer));
369}
370
Tom Josepha891eb72017-03-14 17:25:01 +0530371void EventLoop::stopSOLPayloadInstance(uint8_t payloadInst)
372{
373 auto iter = payloadInfo.find(payloadInst);
374 if (iter == payloadInfo.end())
375 {
376 log<level::ERR>("SOL Payload instance not found",
377 entry("payloadInst=%d", payloadInst));
378 throw std::runtime_error("SOL payload instance not found");
379 }
380
381 int rc = 0;
382
383 /* Destroy the character accumulate timer event source */
384 rc = sd_event_source_set_enabled(
385 (std::get<0>(iter->second.at(Timers::ACCUMULATE))).get(),
386 SD_EVENT_OFF);
387 if (rc < 0)
388 {
389 log<level::ERR>("Failed to disable the character accumulate timer",
390 entry("RC=%d", rc));
391 payloadInfo.erase(payloadInst);
392 throw std::runtime_error("Failed to disable accumulate timer");
393 }
394
395 /* Destroy the retry interval timer event source */
396 rc = sd_event_source_set_enabled(
397 (std::get<0>(iter->second.at(Timers::RETRY))).get(),
398 SD_EVENT_OFF);
399 if (rc < 0)
400 {
401 log<level::ERR>("Failed to disable the retry timer",
402 entry("RC=%d", rc));
403 payloadInfo.erase(payloadInst);
404 throw std::runtime_error("Failed to disable retry timer");
405 }
406
407 payloadInfo.erase(payloadInst);
408}
409
Tom Joseph0a0d8f62017-03-14 17:04:58 +0530410void EventLoop::switchTimer(uint8_t payloadInst,
411 Timers type,
412 bool status)
413{
414 auto iter = payloadInfo.find(payloadInst);
415 if (iter == payloadInfo.end())
416 {
417 log<level::ERR>("SOL Payload instance not found",
418 entry("payloadInst=%d", payloadInst));
419 throw std::runtime_error("SOL Payload instance not found");
420 }
421
422 int rc = 0;
423 auto source = (std::get<0>(iter->second.at(type))).get();
424 auto interval = std::get<1>(iter->second.at(type));
425
426 // Turn OFF the timer
427 if (!status)
428 {
429 rc = sd_event_source_set_enabled(source, SD_EVENT_OFF);
430 if (rc < 0)
431 {
432 log<level::ERR>("Failed to disable the timer", entry("RC=%d", rc));
433 throw std::runtime_error("Failed to disable timer");
434 }
435 return;
436 }
437
438 // Turn ON the timer
439 uint64_t currentTime = 0;
440 rc = sd_event_now(event, CLOCK_MONOTONIC, &currentTime);
441 if (rc < 0)
442 {
443 log<level::ERR>("Failed to get the current timestamp",
444 entry("RC=%d", rc));
445 throw std::runtime_error("Failed to get current timestamp");
446 }
447
448 rc = sd_event_source_set_time(source, currentTime + interval.count());
449 if (rc < 0)
450 {
451 log<level::ERR>("sd_event_source_set_time function failed",
452 entry("RC=%d", rc));
453 throw std::runtime_error("sd_event_source_set_time function failed");
454 }
455
456 rc = sd_event_source_set_enabled(source, SD_EVENT_ONESHOT);
457 if (rc < 0)
458 {
459 log<level::ERR>("Failed to enable the timer", entry("RC=%d",rc));
460 throw std::runtime_error("Failed to enable timer");
461 }
462}
463
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530464} // namespace eventloop