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             context.resendPayload(sol::Context::noClear);
153         }
154         else
155         {
156             context.retryCounter = context.maxRetryCount;
157             context.resendPayload(sol::Context::clear);
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         }
285 
286         hostConsole.reset();
287     }
288 }
289 
290 void 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 
369 void 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 
408 void 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 
462 } // namespace eventloop
463