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