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