blob: 0272e6c06031a804494a8b43000995c358142f0e [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;
106 auto bufferSize = std::get<sol::Manager&>(singletonPool).dataBuffer.size();
107
108 try
109 {
110 if(bufferSize > 0)
111 {
112 // Send the SOL payload
113 }
114
115 std::get<eventloop::EventLoop&>(singletonPool).switchTimer(
116 instance, Timers::ACCUMULATE, true);
117 }
118 catch (std::exception& e)
119 {
120 log<level::ERR>(e.what());
121 }
122
123 return 0;
124}
125
126static int retryTimerHandler(sd_event_source* s, uint64_t usec,
127 void *userdata)
128{
129 // The instance is hardcoded to 1, in the case of supporting multiple
130 // payload instances we would need to populate it from userdata
131 uint8_t instance = 1;
132
133 try
134 {
135 auto& context = std::get<sol::Manager&>(singletonPool).getContext
136 (instance);
137
138 if (context.retryCounter)
139 {
140 --context.retryCounter;
141 std::get<eventloop::EventLoop&>(singletonPool).switchTimer
142 (instance, Timers::RETRY, true);
143 // Resend the SOL payload
144 }
145 else
146 {
147 context.retryCounter = context.maxRetryCount;
148 // Resend the SOL payload
149 std::get<eventloop::EventLoop&>(singletonPool).switchTimer
150 (instance, Timers::RETRY, false);
151 std::get<eventloop::EventLoop&>(singletonPool).switchTimer
152 (instance, Timers::ACCUMULATE, true);
153 }
154 }
155 catch (std::exception& e)
156 {
157 log<level::ERR>(e.what());
158 }
159
160 return 0;
161}
162
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530163int EventLoop::startEventLoop()
164{
165 int fd = -1;
166 int r = 0;
167 sigset_t ss;
168 sd_event_source* source = nullptr;
169
170 r = sd_event_default(&event);
171 if (r < 0)
172 {
173 goto finish;
174 }
175
176 if (sigemptyset(&ss) < 0 || sigaddset(&ss, SIGTERM) < 0 ||
177 sigaddset(&ss, SIGINT) < 0)
178 {
179 r = -errno;
180 goto finish;
181 }
182
183 /* Block SIGTERM first, so that the event loop can handle it */
184 if (sigprocmask(SIG_BLOCK, &ss, nullptr) < 0)
185 {
186 r = -errno;
187 goto finish;
188 }
189
190 /* Let's make use of the default handler and "floating" reference features
191 * of sd_event_add_signal() */
192 r = sd_event_add_signal(event, nullptr, SIGTERM, nullptr, nullptr);
193 if (r < 0)
194 {
195 goto finish;
196 }
197
198 r = sd_event_add_signal(event, nullptr, SIGINT, nullptr, nullptr);
199 if (r < 0)
200 {
201 goto finish;
202 }
203
204 if (sd_listen_fds(0) != 1)
205 {
206 log<level::ERR>("No or too many file descriptors received");
207 goto finish;
208 }
209
210 fd = SD_LISTEN_FDS_START;
211
212 r = sd_event_add_io(event, &source, fd, EPOLLIN, udp623Handler, nullptr);
213 if (r < 0)
214 {
215 goto finish;
216 }
217
218 udpIPMI.reset(source);
219 source = nullptr;
220
221 r = sd_event_loop(event);
222
223finish:
224 event = sd_event_unref(event);
225
226 if (fd >= 0)
227 {
228 (void) close(fd);
229 }
230
231 if (r < 0)
232 {
233 log<level::ERR>("Event Loop Failure:",
234 entry("FAILURE=%s", strerror(-r)));
235 }
236
237 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
238}
239
Tom Joseph56f24e92017-03-14 16:06:31 +0530240void EventLoop::startHostConsole(const sol::CustomFD& fd)
241{
242 int rc = 0;
243
244 if((fd() == -1) || hostConsole.get())
245 {
246 throw std::runtime_error("Console descriptor already added");
247 }
248
249 sd_event_source* source = nullptr;
250
251 // Add the fd to the event loop for EPOLLIN
252 rc = sd_event_add_io(
253 event, &source, fd(), EPOLLIN, consoleInputHandler, nullptr);
254 if (rc < 0)
255 {
256 throw std::runtime_error("Failed to add socket descriptor");
257 }
258
259 hostConsole.reset(source);
260 source=nullptr;
261}
262
Tom Josephcfbd43f2017-03-14 16:14:03 +0530263void EventLoop::stopHostConsole()
264{
265 int rc = 0;
266
267 if (hostConsole.get())
268 {
269 // Disable the host console payload
270 rc = sd_event_source_set_enabled(hostConsole.get(), SD_EVENT_OFF);
271 if (rc < 0)
272 {
273 log<level::ERR>("Failed to disable the host console socket",
274 entry("RC=%d", rc));
275 hostConsole.reset();
276 throw std::runtime_error("Failed to disable socket descriptor");
277 }
278
279 hostConsole.reset();
280 }
281}
282
Tom Josephe989c362017-03-14 17:16:50 +0530283void EventLoop::startSOLPayloadInstance(uint8_t payloadInst,
284 IntervalType accumulateInterval,
285 IntervalType retryInterval)
286{
287 auto instance = payloadInst;
288 sd_event_source* accTimerSource = nullptr;
289 sd_event_source* retryTimerSource = nullptr;
290 int rc = 0;
291 uint64_t currentTime = 0;
292
293 rc = sd_event_now(event, CLOCK_MONOTONIC, &currentTime);
294 if (rc < 0)
295 {
296 log<level::ERR>("Failed to get the current timestamp",
297 entry("RC=%d", rc));
298 throw std::runtime_error("Failed to get current timestamp");
299 }
300
301 // Create character accumulate timer
302 rc = sd_event_add_time(event,
303 &accTimerSource,
304 CLOCK_MONOTONIC,
305 currentTime + accumulateInterval.count(),
306 0,
307 charAccTimerHandler,
308 static_cast<void *>(&instance));
309 if (rc < 0)
310 {
311 log<level::ERR>("Failed to setup the accumulate timer",
312 entry("RC = %d", rc));
313 throw std::runtime_error("Failed to setup accumulate timer");
314 }
315
316 // Create retry interval timer and add to the event loop
317 rc = sd_event_add_time(event,
318 &retryTimerSource,
319 CLOCK_MONOTONIC,
320 currentTime + retryInterval.count(),
321 0,
322 retryTimerHandler,
323 static_cast<void *>(&instance));
324 if (rc < 0)
325 {
326 log<level::ERR>("Failed to setup the retry timer",
327 entry("RC = %d", rc));
328 throw std::runtime_error("Failed to setup retry timer");
329 }
330
331 // Enable the Character Accumulate Timer
332 rc = sd_event_source_set_enabled(accTimerSource, SD_EVENT_ONESHOT);
333 if (rc < 0)
334 {
335 log<level::ERR>("Failed to enable the accumulate timer",
336 entry("rc = %d", rc));
337 throw std::runtime_error("Failed to enable accumulate timer");
338 }
339
340 // Disable the Retry Interval Timer
341 rc = sd_event_source_set_enabled(retryTimerSource, SD_EVENT_OFF);
342 if (rc < 0)
343 {
344 log<level::ERR>("Failed to disable the retry timer",
345 entry("RC = %d", rc));
346 throw std::runtime_error("Failed to disable retry timer");
347 }
348
349 EventSource accEventSource(accTimerSource);
350 EventSource retryEventSource(retryTimerSource);
351 accTimerSource = nullptr;
352 retryTimerSource = nullptr;
353
354 TimerMap timer;
355 timer.emplace(Timers::ACCUMULATE, std::make_tuple(std::move(accEventSource),
356 accumulateInterval));
357 timer.emplace(Timers::RETRY, std::make_tuple(std::move(retryEventSource),
358 retryInterval));
359 payloadInfo.emplace(instance, std::move(timer));
360}
361
Tom Josepha891eb72017-03-14 17:25:01 +0530362void EventLoop::stopSOLPayloadInstance(uint8_t payloadInst)
363{
364 auto iter = payloadInfo.find(payloadInst);
365 if (iter == payloadInfo.end())
366 {
367 log<level::ERR>("SOL Payload instance not found",
368 entry("payloadInst=%d", payloadInst));
369 throw std::runtime_error("SOL payload instance not found");
370 }
371
372 int rc = 0;
373
374 /* Destroy the character accumulate timer event source */
375 rc = sd_event_source_set_enabled(
376 (std::get<0>(iter->second.at(Timers::ACCUMULATE))).get(),
377 SD_EVENT_OFF);
378 if (rc < 0)
379 {
380 log<level::ERR>("Failed to disable the character accumulate timer",
381 entry("RC=%d", rc));
382 payloadInfo.erase(payloadInst);
383 throw std::runtime_error("Failed to disable accumulate timer");
384 }
385
386 /* Destroy the retry interval timer event source */
387 rc = sd_event_source_set_enabled(
388 (std::get<0>(iter->second.at(Timers::RETRY))).get(),
389 SD_EVENT_OFF);
390 if (rc < 0)
391 {
392 log<level::ERR>("Failed to disable the retry timer",
393 entry("RC=%d", rc));
394 payloadInfo.erase(payloadInst);
395 throw std::runtime_error("Failed to disable retry timer");
396 }
397
398 payloadInfo.erase(payloadInst);
399}
400
Tom Joseph0a0d8f62017-03-14 17:04:58 +0530401void EventLoop::switchTimer(uint8_t payloadInst,
402 Timers type,
403 bool status)
404{
405 auto iter = payloadInfo.find(payloadInst);
406 if (iter == payloadInfo.end())
407 {
408 log<level::ERR>("SOL Payload instance not found",
409 entry("payloadInst=%d", payloadInst));
410 throw std::runtime_error("SOL Payload instance not found");
411 }
412
413 int rc = 0;
414 auto source = (std::get<0>(iter->second.at(type))).get();
415 auto interval = std::get<1>(iter->second.at(type));
416
417 // Turn OFF the timer
418 if (!status)
419 {
420 rc = sd_event_source_set_enabled(source, SD_EVENT_OFF);
421 if (rc < 0)
422 {
423 log<level::ERR>("Failed to disable the timer", entry("RC=%d", rc));
424 throw std::runtime_error("Failed to disable timer");
425 }
426 return;
427 }
428
429 // Turn ON the timer
430 uint64_t currentTime = 0;
431 rc = sd_event_now(event, CLOCK_MONOTONIC, &currentTime);
432 if (rc < 0)
433 {
434 log<level::ERR>("Failed to get the current timestamp",
435 entry("RC=%d", rc));
436 throw std::runtime_error("Failed to get current timestamp");
437 }
438
439 rc = sd_event_source_set_time(source, currentTime + interval.count());
440 if (rc < 0)
441 {
442 log<level::ERR>("sd_event_source_set_time function failed",
443 entry("RC=%d", rc));
444 throw std::runtime_error("sd_event_source_set_time function failed");
445 }
446
447 rc = sd_event_source_set_enabled(source, SD_EVENT_ONESHOT);
448 if (rc < 0)
449 {
450 log<level::ERR>("Failed to enable the timer", entry("RC=%d",rc));
451 throw std::runtime_error("Failed to enable timer");
452 }
453}
454
Tom Joseph7fd26dd2017-03-14 15:26:26 +0530455} // namespace eventloop