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