1 #include "sd_event_loop.hpp"
2 
3 #include "main.hpp"
4 #include "message_handler.hpp"
5 
6 #include <netinet/in.h>
7 #include <sys/ioctl.h>
8 #include <sys/socket.h>
9 #include <systemd/sd-daemon.h>
10 
11 #include <boost/asio/io_context.hpp>
12 #include <phosphor-logging/log.hpp>
13 #include <sdbusplus/asio/sd_event.hpp>
14 
15 namespace eventloop
16 {
17 using namespace phosphor::logging;
18 
19 static int udp623Handler(sd_event_source* es, int fd, uint32_t revents,
20                          void* userdata)
21 {
22     std::shared_ptr<udpsocket::Channel> channelPtr;
23     struct timeval timeout;
24     timeout.tv_sec = SELECT_CALL_TIMEOUT;
25     timeout.tv_usec = 0;
26 
27     try
28     {
29         channelPtr.reset(new udpsocket::Channel(fd, timeout));
30 
31         // Initialize the Message Handler with the socket channel
32         message::Handler msgHandler(channelPtr);
33 
34         // Read the incoming IPMI packet
35         std::shared_ptr<message::Message> inMessage(msgHandler.receive());
36         if (inMessage == nullptr)
37         {
38             return 0;
39         }
40 
41         // Execute the Command
42         auto outMessage = msgHandler.executeCommand(inMessage);
43         if (outMessage == nullptr)
44         {
45             return 0;
46         }
47 
48         // Send the response IPMI Message
49         msgHandler.send(outMessage);
50     }
51     catch (std::exception& e)
52     {
53         log<level::ERR>("Executing the IPMI message failed");
54         log<level::ERR>(e.what());
55     }
56 
57     return 0;
58 }
59 
60 static int consoleInputHandler(sd_event_source* es, int fd, uint32_t revents,
61                                void* userdata)
62 {
63     try
64     {
65         int readSize = 0;
66 
67         if (ioctl(fd, FIONREAD, &readSize) < 0)
68         {
69             log<level::ERR>("ioctl failed for FIONREAD:",
70                             entry("ERRNO=%d", errno));
71             return 0;
72         }
73 
74         std::vector<uint8_t> buffer(readSize);
75         auto bufferSize = buffer.size();
76         ssize_t readDataLen = 0;
77 
78         readDataLen = read(fd, buffer.data(), bufferSize);
79 
80         // Update the Console buffer with data read from the socket
81         if (readDataLen > 0)
82         {
83             buffer.resize(readDataLen);
84             std::get<sol::Manager&>(singletonPool).dataBuffer.write(buffer);
85         }
86         else if (readDataLen == 0)
87         {
88             log<level::ERR>("Connection Closed for host console socket");
89         }
90         else if (readDataLen < 0) // Error
91         {
92             log<level::ERR>("Reading from host console socket failed:",
93                             entry("ERRNO=%d", errno));
94         }
95     }
96     catch (std::exception& e)
97     {
98         log<level::ERR>(e.what());
99     }
100 
101     return 0;
102 }
103 
104 static int charAccTimerHandler(sd_event_source* s, uint64_t usec,
105                                void* userdata)
106 {
107     auto bufferSize = std::get<sol::Manager&>(singletonPool).dataBuffer.size();
108 
109     try
110     {
111         // The instance is hardcoded to 1, in the case of supporting multiple
112         // payload instances we would need to populate it from userdata
113         uint8_t instance = 1;
114 
115         if (bufferSize > 0)
116         {
117             auto& context =
118                 std::get<sol::Manager&>(singletonPool).getContext(instance);
119 
120             int rc = context.sendOutboundPayload();
121 
122             if (rc == 0)
123             {
124                 return 0;
125             }
126         }
127 
128         std::get<eventloop::EventLoop&>(singletonPool)
129             .switchTimer(instance, Timers::ACCUMULATE, true);
130     }
131     catch (std::exception& e)
132     {
133         log<level::ERR>(e.what());
134     }
135 
136     return 0;
137 }
138 
139 static int retryTimerHandler(sd_event_source* s, uint64_t usec, void* userdata)
140 {
141     try
142     {
143         // The instance is hardcoded to 1, in the case of supporting multiple
144         // payload instances we would need to populate it from userdata
145         uint8_t instance = 1;
146 
147         auto& context =
148             std::get<sol::Manager&>(singletonPool).getContext(instance);
149 
150         if (context.retryCounter)
151         {
152             --context.retryCounter;
153             std::get<eventloop::EventLoop&>(singletonPool)
154                 .switchTimer(instance, Timers::RETRY, true);
155             context.resendPayload(sol::Context::noClear);
156         }
157         else
158         {
159             context.retryCounter = context.maxRetryCount;
160             context.resendPayload(sol::Context::clear);
161             std::get<eventloop::EventLoop&>(singletonPool)
162                 .switchTimer(instance, Timers::RETRY, false);
163             std::get<eventloop::EventLoop&>(singletonPool)
164                 .switchTimer(instance, Timers::ACCUMULATE, true);
165         }
166     }
167     catch (std::exception& e)
168     {
169         log<level::ERR>(e.what());
170     }
171 
172     return 0;
173 }
174 
175 int EventLoop::startEventLoop()
176 {
177     int fd = -1;
178     int r = 0;
179     int listen_fd;
180     sigset_t ss;
181     sd_event_source* source = nullptr;
182 
183     sdbusplus::asio::sd_event_wrapper sdEvents(*io);
184     event = sdEvents.get();
185 
186     if (sigemptyset(&ss) < 0 || sigaddset(&ss, SIGTERM) < 0 ||
187         sigaddset(&ss, SIGINT) < 0)
188     {
189         r = -errno;
190         return EXIT_FAILURE;
191     }
192 
193     /* Block SIGTERM first, so that the event loop can handle it */
194     if (sigprocmask(SIG_BLOCK, &ss, nullptr) < 0)
195     {
196         r = -errno;
197         return EXIT_FAILURE;
198     }
199 
200     /* Let's make use of the default handler and "floating" reference features
201      * of sd_event_add_signal() */
202     r = sd_event_add_signal(event, nullptr, SIGTERM, nullptr, nullptr);
203     if (r < 0)
204     {
205         return EXIT_FAILURE;
206     }
207 
208     r = sd_event_add_signal(event, nullptr, SIGINT, nullptr, nullptr);
209     if (r < 0)
210     {
211         return EXIT_FAILURE;
212     }
213 
214     // Create our own socket if SysD did not supply one.
215     listen_fd = sd_listen_fds(0);
216     if (listen_fd == 1)
217     {
218         fd = SD_LISTEN_FDS_START;
219     }
220     else if (listen_fd > 1)
221     {
222         log<level::ERR>("Too many file descriptors received");
223         return 1;
224     }
225     else
226     {
227         struct sockaddr_in address;
228         if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == 0)
229         {
230             r = -errno;
231             log<level::ERR>("Unable to manually open socket");
232             return EXIT_FAILURE;
233         }
234 
235         address.sin_family = AF_INET;
236         address.sin_addr.s_addr = INADDR_ANY;
237         address.sin_port = htons(IPMI_STD_PORT);
238 
239         if (bind(fd, (struct sockaddr*)&address, sizeof(address)) < 0)
240         {
241             r = -errno;
242             log<level::ERR>("Unable to bind socket");
243             close(fd);
244             return EXIT_FAILURE;
245         }
246     }
247 
248     r = sd_event_add_io(event, &source, fd, EPOLLIN, udp623Handler, nullptr);
249     if (r < 0)
250     {
251         close(fd);
252         return EXIT_FAILURE;
253     }
254 
255     udpIPMI.reset(source);
256     source = nullptr;
257 
258     io->run();
259 
260     return EXIT_SUCCESS;
261 }
262 
263 void EventLoop::startHostConsole(const sol::CustomFD& fd)
264 {
265     int rc = 0;
266 
267     if ((fd() == -1) || hostConsole.get())
268     {
269         throw std::runtime_error("Console descriptor already added");
270     }
271 
272     sd_event_source* source = nullptr;
273 
274     // Add the fd to the event loop for EPOLLIN
275     rc = sd_event_add_io(event, &source, fd(), EPOLLIN, consoleInputHandler,
276                          nullptr);
277     if (rc < 0)
278     {
279         throw std::runtime_error("Failed to add socket descriptor");
280     }
281 
282     hostConsole.reset(source);
283     source = nullptr;
284 }
285 
286 void EventLoop::stopHostConsole()
287 {
288     if (hostConsole.get())
289     {
290         // Disable the host console payload
291         int rc = sd_event_source_set_enabled(hostConsole.get(), SD_EVENT_OFF);
292         if (rc < 0)
293         {
294             log<level::ERR>("Failed to disable the host console socket",
295                             entry("RC=%d", rc));
296         }
297 
298         hostConsole.reset();
299     }
300 }
301 
302 void EventLoop::startSOLPayloadInstance(uint8_t payloadInst,
303                                         IntervalType accumulateInterval,
304                                         IntervalType retryInterval)
305 {
306     auto instance = payloadInst;
307     sd_event_source* accTimerSource = nullptr;
308     sd_event_source* retryTimerSource = nullptr;
309     int rc = 0;
310     uint64_t currentTime = 0;
311 
312     rc = sd_event_now(event, CLOCK_MONOTONIC, &currentTime);
313     if (rc < 0)
314     {
315         log<level::ERR>("Failed to get the current timestamp",
316                         entry("RC=%d", rc));
317         throw std::runtime_error("Failed to get current timestamp");
318     }
319 
320     // Create character accumulate timer
321     rc = sd_event_add_time(event, &accTimerSource, CLOCK_MONOTONIC,
322                            currentTime + accumulateInterval.count(), 0,
323                            charAccTimerHandler, 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, &retryTimerSource, CLOCK_MONOTONIC,
333                            currentTime + retryInterval.count(), 0,
334                            retryTimerHandler, static_cast<void*>(&instance));
335     if (rc < 0)
336     {
337         log<level::ERR>("Failed to setup the retry timer", entry("RC=%d", rc));
338         throw std::runtime_error("Failed to setup retry timer");
339     }
340 
341     // Enable the Character Accumulate Timer
342     rc = sd_event_source_set_enabled(accTimerSource, SD_EVENT_ONESHOT);
343     if (rc < 0)
344     {
345         log<level::ERR>("Failed to enable the accumulate timer",
346                         entry("RC=%d", rc));
347         throw std::runtime_error("Failed to enable accumulate timer");
348     }
349 
350     // Disable the Retry Interval Timer
351     rc = sd_event_source_set_enabled(retryTimerSource, SD_EVENT_OFF);
352     if (rc < 0)
353     {
354         log<level::ERR>("Failed to disable the retry timer",
355                         entry("RC=%d", rc));
356         throw std::runtime_error("Failed to disable retry timer");
357     }
358 
359     EventSource accEventSource(accTimerSource);
360     EventSource retryEventSource(retryTimerSource);
361     accTimerSource = nullptr;
362     retryTimerSource = nullptr;
363 
364     TimerMap timer;
365     timer.emplace(Timers::ACCUMULATE, std::make_tuple(std::move(accEventSource),
366                                                       accumulateInterval));
367     timer.emplace(Timers::RETRY,
368                   std::make_tuple(std::move(retryEventSource), retryInterval));
369     payloadInfo.emplace(instance, std::move(timer));
370 }
371 
372 void EventLoop::stopSOLPayloadInstance(uint8_t payloadInst)
373 {
374     auto iter = payloadInfo.find(payloadInst);
375     if (iter == payloadInfo.end())
376     {
377         log<level::ERR>("SOL Payload instance not found",
378                         entry("PAYLOADINST=%d", payloadInst));
379         throw std::runtime_error("SOL payload instance not found");
380     }
381 
382     int rc = 0;
383 
384     /* Destroy the character accumulate timer event source */
385     rc = sd_event_source_set_enabled(
386         (std::get<0>(iter->second.at(Timers::ACCUMULATE))).get(), 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(), SD_EVENT_OFF);
398     if (rc < 0)
399     {
400         log<level::ERR>("Failed to disable the retry timer",
401                         entry("RC=%d", rc));
402         payloadInfo.erase(payloadInst);
403         throw std::runtime_error("Failed to disable retry timer");
404     }
405 
406     payloadInfo.erase(payloadInst);
407 }
408 
409 void EventLoop::switchTimer(uint8_t payloadInst, Timers type, bool status)
410 {
411     auto iter = payloadInfo.find(payloadInst);
412     if (iter == payloadInfo.end())
413     {
414         log<level::ERR>("SOL Payload instance not found",
415                         entry("PAYLOADINST=%d", payloadInst));
416         throw std::runtime_error("SOL Payload instance not found");
417     }
418 
419     int rc = 0;
420     auto source = (std::get<0>(iter->second.at(type))).get();
421     auto interval = std::get<1>(iter->second.at(type));
422 
423     // Turn OFF the timer
424     if (!status)
425     {
426         rc = sd_event_source_set_enabled(source, SD_EVENT_OFF);
427         if (rc < 0)
428         {
429             log<level::ERR>("Failed to disable the timer", entry("RC=%d", rc));
430             throw std::runtime_error("Failed to disable timer");
431         }
432         return;
433     }
434 
435     // Turn ON the timer
436     uint64_t currentTime = 0;
437     rc = sd_event_now(event, CLOCK_MONOTONIC, &currentTime);
438     if (rc < 0)
439     {
440         log<level::ERR>("Failed to get the current timestamp",
441                         entry("RC=%d", rc));
442         throw std::runtime_error("Failed to get current timestamp");
443     }
444 
445     rc = sd_event_source_set_time(source, currentTime + interval.count());
446     if (rc < 0)
447     {
448         log<level::ERR>("sd_event_source_set_time function failed",
449                         entry("RC=%d", rc));
450         throw std::runtime_error("sd_event_source_set_time function failed");
451     }
452 
453     rc = sd_event_source_set_enabled(source, SD_EVENT_ONESHOT);
454     if (rc < 0)
455     {
456         log<level::ERR>("Failed to enable the timer", entry("RC=%d", rc));
457         throw std::runtime_error("Failed to enable timer");
458     }
459 }
460 
461 } // namespace eventloop
462