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     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 
126 static 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 
163 int 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 
223 finish:
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 
240 void 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 
263 void 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 
283 void 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 
362 void EventLoop::switchTimer(uint8_t payloadInst,
363                             Timers type,
364                             bool status)
365 {
366     auto iter = payloadInfo.find(payloadInst);
367     if (iter == payloadInfo.end())
368     {
369         log<level::ERR>("SOL Payload instance not found",
370                 entry("payloadInst=%d", payloadInst));
371         throw std::runtime_error("SOL Payload instance not found");
372     }
373 
374     int rc = 0;
375     auto source = (std::get<0>(iter->second.at(type))).get();
376     auto interval = std::get<1>(iter->second.at(type));
377 
378     // Turn OFF the timer
379     if (!status)
380     {
381         rc = sd_event_source_set_enabled(source, SD_EVENT_OFF);
382         if (rc < 0)
383         {
384             log<level::ERR>("Failed to disable the timer", entry("RC=%d", rc));
385             throw std::runtime_error("Failed to disable timer");
386         }
387         return;
388     }
389 
390     // Turn ON the timer
391     uint64_t currentTime = 0;
392     rc = sd_event_now(event, CLOCK_MONOTONIC, &currentTime);
393     if (rc < 0)
394     {
395         log<level::ERR>("Failed to get the current timestamp",
396                 entry("RC=%d", rc));
397         throw std::runtime_error("Failed to get current timestamp");
398     }
399 
400     rc = sd_event_source_set_time(source, currentTime + interval.count());
401     if (rc < 0)
402     {
403         log<level::ERR>("sd_event_source_set_time function failed",
404                 entry("RC=%d", rc));
405         throw std::runtime_error("sd_event_source_set_time function failed");
406     }
407 
408     rc = sd_event_source_set_enabled(source, SD_EVENT_ONESHOT);
409     if (rc < 0)
410     {
411         log<level::ERR>("Failed to enable the timer", entry("RC=%d",rc));
412         throw std::runtime_error("Failed to enable timer");
413     }
414 }
415 
416 } // namespace eventloop
417