1 #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 
8 namespace eventloop
9 {
10 using namespace phosphor::logging;
11 
12 static 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 
56 static 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 
100 static 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     int rc = 0;
107     auto bufferSize = std::get<sol::Manager&>(singletonPool).dataBuffer.size();
108 
109     try
110     {
111         if(bufferSize > 0)
112         {
113             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             }
122         }
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 
135 static 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);
152             // Resend the SOL payload
153         }
154         else
155         {
156             context.retryCounter = context.maxRetryCount;
157             // Resend the SOL payload
158             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 
172 int 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 
232 finish:
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 
249 void 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 
272 void 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 
292 void 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 
371 void 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 
410 void 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 
464 } // namespace eventloop
465