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     auto bus = ipmid_get_sd_bus_connection();
179 
180     r = sd_event_default(&event);
181     if (r < 0)
182     {
183         goto finish;
184     }
185 
186     // Attach the bus to sd_event to service user requests
187     r = sd_bus_attach_event(bus, event, SD_EVENT_PRIORITY_NORMAL);
188     if (r < 0)
189     {
190         goto finish;
191     }
192 
193     if (sigemptyset(&ss) < 0 || sigaddset(&ss, SIGTERM) < 0 ||
194         sigaddset(&ss, SIGINT) < 0)
195     {
196         r = -errno;
197         goto finish;
198     }
199 
200     /* Block SIGTERM first, so that the event loop can handle it */
201     if (sigprocmask(SIG_BLOCK, &ss, nullptr) < 0)
202     {
203         r = -errno;
204         goto finish;
205     }
206 
207     /* Let's make use of the default handler and "floating" reference features
208      * of sd_event_add_signal() */
209     r = sd_event_add_signal(event, nullptr, SIGTERM, nullptr, nullptr);
210     if (r < 0)
211     {
212         goto finish;
213     }
214 
215     r = sd_event_add_signal(event, nullptr, SIGINT, nullptr, nullptr);
216     if (r < 0)
217     {
218         goto finish;
219     }
220 
221     if (sd_listen_fds(0) != 1)
222     {
223         log<level::ERR>("No or too many file descriptors received");
224         goto finish;
225     }
226 
227     fd = SD_LISTEN_FDS_START;
228 
229     r = sd_event_add_io(event, &source, fd, EPOLLIN, udp623Handler, nullptr);
230     if (r < 0)
231     {
232         goto finish;
233     }
234 
235     udpIPMI.reset(source);
236     source = nullptr;
237 
238     r = sd_event_loop(event);
239 
240 finish:
241     event = sd_event_unref(event);
242 
243     if (fd >= 0)
244     {
245         (void) close(fd);
246     }
247 
248     if (r < 0)
249     {
250         log<level::ERR>("Event Loop Failure:",
251                 entry("FAILURE=%s", strerror(-r)));
252     }
253 
254     return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
255 }
256 
257 void EventLoop::startHostConsole(const sol::CustomFD& fd)
258 {
259     int rc = 0;
260 
261     if((fd() == -1) || hostConsole.get())
262     {
263         throw std::runtime_error("Console descriptor already added");
264     }
265 
266     sd_event_source* source = nullptr;
267 
268     // Add the fd to the event loop for EPOLLIN
269     rc = sd_event_add_io(
270             event, &source, fd(), EPOLLIN, consoleInputHandler, nullptr);
271     if (rc < 0)
272     {
273         throw std::runtime_error("Failed to add socket descriptor");
274     }
275 
276     hostConsole.reset(source);
277     source=nullptr;
278 }
279 
280 void EventLoop::stopHostConsole()
281 {
282     int rc = 0;
283 
284     if (hostConsole.get())
285     {
286         // Disable the host console payload
287         rc = sd_event_source_set_enabled(hostConsole.get(), SD_EVENT_OFF);
288         if (rc < 0)
289         {
290             log<level::ERR>("Failed to disable the host console socket",
291                     entry("RC=%d", rc));
292         }
293 
294         hostConsole.reset();
295     }
296 }
297 
298 void EventLoop::startSOLPayloadInstance(uint8_t payloadInst,
299                                         IntervalType accumulateInterval,
300                                         IntervalType retryInterval)
301 {
302     auto instance = payloadInst;
303     sd_event_source* accTimerSource = nullptr;
304     sd_event_source* retryTimerSource = nullptr;
305     int rc = 0;
306     uint64_t currentTime = 0;
307 
308     rc = sd_event_now(event, CLOCK_MONOTONIC, &currentTime);
309     if (rc < 0)
310     {
311         log<level::ERR>("Failed to get the current timestamp",
312                 entry("RC=%d", rc));
313         throw std::runtime_error("Failed to get current timestamp");
314     }
315 
316     // Create character accumulate timer
317     rc = sd_event_add_time(event,
318                            &accTimerSource,
319                            CLOCK_MONOTONIC,
320                            currentTime + accumulateInterval.count(),
321                            0,
322                            charAccTimerHandler,
323                            static_cast<void *>(&instance));
324     if (rc < 0)
325     {
326         log<level::ERR>("Failed to setup the accumulate timer",
327                 entry("RC=%d", rc));
328         throw std::runtime_error("Failed to setup accumulate timer");
329     }
330 
331     // Create retry interval timer and add to the event loop
332     rc = sd_event_add_time(event,
333                            &retryTimerSource,
334                            CLOCK_MONOTONIC,
335                            currentTime + retryInterval.count(),
336                            0,
337                            retryTimerHandler,
338                            static_cast<void *>(&instance));
339     if (rc < 0)
340     {
341         log<level::ERR>("Failed to setup the retry timer",
342                 entry("RC=%d", rc));
343         throw std::runtime_error("Failed to setup retry timer");
344     }
345 
346     // Enable the Character Accumulate Timer
347     rc = sd_event_source_set_enabled(accTimerSource, SD_EVENT_ONESHOT);
348     if (rc < 0)
349     {
350         log<level::ERR>("Failed to enable the accumulate timer",
351                 entry("RC=%d", rc));
352         throw std::runtime_error("Failed to enable accumulate timer");
353     }
354 
355     // Disable the Retry Interval Timer
356     rc = sd_event_source_set_enabled(retryTimerSource, SD_EVENT_OFF);
357     if (rc < 0)
358     {
359         log<level::ERR>("Failed to disable the retry timer",
360                 entry("RC=%d", rc));
361         throw std::runtime_error("Failed to disable retry timer");
362     }
363 
364     EventSource accEventSource(accTimerSource);
365     EventSource retryEventSource(retryTimerSource);
366     accTimerSource = nullptr;
367     retryTimerSource = nullptr;
368 
369     TimerMap timer;
370     timer.emplace(Timers::ACCUMULATE, std::make_tuple(std::move(accEventSource),
371                                                       accumulateInterval));
372     timer.emplace(Timers::RETRY, std::make_tuple(std::move(retryEventSource),
373                                                  retryInterval));
374     payloadInfo.emplace(instance, std::move(timer));
375 }
376 
377 void EventLoop::stopSOLPayloadInstance(uint8_t payloadInst)
378 {
379     auto iter = payloadInfo.find(payloadInst);
380     if (iter == payloadInfo.end())
381     {
382         log<level::ERR>("SOL Payload instance not found",
383                 entry("PAYLOADINST=%d", payloadInst));
384         throw std::runtime_error("SOL payload instance not found");
385     }
386 
387     int rc = 0;
388 
389     /* Destroy the character accumulate timer event source */
390     rc = sd_event_source_set_enabled(
391             (std::get<0>(iter->second.at(Timers::ACCUMULATE))).get(),
392             SD_EVENT_OFF);
393     if (rc < 0)
394     {
395         log<level::ERR>("Failed to disable the character accumulate timer",
396                 entry("RC=%d", rc));
397         payloadInfo.erase(payloadInst);
398         throw std::runtime_error("Failed to disable accumulate timer");
399     }
400 
401     /* Destroy the retry interval timer event source */
402     rc = sd_event_source_set_enabled(
403             (std::get<0>(iter->second.at(Timers::RETRY))).get(),
404             SD_EVENT_OFF);
405     if (rc < 0)
406     {
407         log<level::ERR>("Failed to disable the retry timer",
408                 entry("RC=%d", rc));
409         payloadInfo.erase(payloadInst);
410         throw std::runtime_error("Failed to disable retry timer");
411     }
412 
413     payloadInfo.erase(payloadInst);
414 }
415 
416 void EventLoop::switchTimer(uint8_t payloadInst,
417                             Timers type,
418                             bool status)
419 {
420     auto iter = payloadInfo.find(payloadInst);
421     if (iter == payloadInfo.end())
422     {
423         log<level::ERR>("SOL Payload instance not found",
424                 entry("PAYLOADINST=%d", payloadInst));
425         throw std::runtime_error("SOL Payload instance not found");
426     }
427 
428     int rc = 0;
429     auto source = (std::get<0>(iter->second.at(type))).get();
430     auto interval = std::get<1>(iter->second.at(type));
431 
432     // Turn OFF the timer
433     if (!status)
434     {
435         rc = sd_event_source_set_enabled(source, SD_EVENT_OFF);
436         if (rc < 0)
437         {
438             log<level::ERR>("Failed to disable the timer", entry("RC=%d", rc));
439             throw std::runtime_error("Failed to disable timer");
440         }
441         return;
442     }
443 
444     // Turn ON the timer
445     uint64_t currentTime = 0;
446     rc = sd_event_now(event, CLOCK_MONOTONIC, &currentTime);
447     if (rc < 0)
448     {
449         log<level::ERR>("Failed to get the current timestamp",
450                 entry("RC=%d", rc));
451         throw std::runtime_error("Failed to get current timestamp");
452     }
453 
454     rc = sd_event_source_set_time(source, currentTime + interval.count());
455     if (rc < 0)
456     {
457         log<level::ERR>("sd_event_source_set_time function failed",
458                 entry("RC=%d", rc));
459         throw std::runtime_error("sd_event_source_set_time function failed");
460     }
461 
462     rc = sd_event_source_set_enabled(source, SD_EVENT_ONESHOT);
463     if (rc < 0)
464     {
465         log<level::ERR>("Failed to enable the timer", entry("RC=%d",rc));
466         throw std::runtime_error("Failed to enable timer");
467     }
468 }
469 
470 } // namespace eventloop
471