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