1 #include <sys/ioctl.h>
2 #include <systemd/sd-daemon.h>
3 #include <phosphor-logging/log.hpp>
4 #include "main.hpp"
5 #include "message_handler.hpp"
6 #include "sd_event_loop.hpp"
7 
8 namespace eventloop
9 {
10 using namespace phosphor::logging;
11 
12 static int udp623Handler(sd_event_source* es, int fd, uint32_t revents,
13                          void* userdata)
14 {
15     std::shared_ptr<udpsocket::Channel> channelPtr;
16     struct timeval timeout;
17     timeout.tv_sec = SELECT_CALL_TIMEOUT;
18     timeout.tv_usec = 0;
19 
20     try
21     {
22         channelPtr.reset(new udpsocket::Channel(fd, timeout));
23 
24         // Initialize the Message Handler with the socket channel
25         message::Handler msgHandler(channelPtr);
26 
27 
28         std::unique_ptr<message::Message> inMessage;
29 
30         // Read the incoming IPMI packet
31         inMessage = msgHandler.receive();
32         if (inMessage == nullptr)
33         {
34             return 0;
35         }
36 
37         // Execute the Command
38         auto outMessage = msgHandler.executeCommand(*(inMessage.get()));
39         if (outMessage == nullptr)
40         {
41             return 0;
42         }
43 
44         // Send the response IPMI Message
45         msgHandler.send(*(outMessage.get()));
46     }
47     catch (std::exception& e)
48     {
49         log<level::ERR>("Executing the IPMI message failed");
50         log<level::ERR>(e.what());
51     }
52 
53     return 0;
54 }
55 
56 static int consoleInputHandler(sd_event_source* es, int fd, uint32_t revents,
57                                void* userdata)
58 {
59     try
60     {
61         int readSize = 0;
62 
63         if (ioctl(fd, FIONREAD, &readSize) < 0)
64         {
65             log<level::ERR>("ioctl failed for FIONREAD:",
66                     entry("errno = %d", errno));
67             return 0;
68         }
69 
70         std::vector<uint8_t> buffer(readSize);
71         auto bufferSize = buffer.size();
72         ssize_t readDataLen = 0;
73 
74         readDataLen = read(fd, buffer.data(), bufferSize);
75 
76         // Update the Console buffer with data read from the socket
77         if (readDataLen > 0)
78         {
79             buffer.resize(readDataLen);
80             std::get<sol::Manager&>(singletonPool).dataBuffer.write(buffer);
81         }
82         else if (readDataLen == 0)
83         {
84             log<level::ERR>("Connection Closed for host console socket");
85         }
86         else if (readDataLen < 0) // Error
87         {
88             log<level::ERR>("Reading from host console socket failed:",
89                     entry("ERRNO=%d", errno));
90         }
91     }
92     catch (std::exception& e)
93     {
94         log<level::ERR>(e.what());
95     }
96 
97     return 0;
98 }
99 
100 int EventLoop::startEventLoop()
101 {
102     int fd = -1;
103     int r = 0;
104     sigset_t ss;
105     sd_event_source* source = nullptr;
106 
107     r = sd_event_default(&event);
108     if (r < 0)
109     {
110         goto finish;
111     }
112 
113     if (sigemptyset(&ss) < 0 || sigaddset(&ss, SIGTERM) < 0 ||
114         sigaddset(&ss, SIGINT) < 0)
115     {
116         r = -errno;
117         goto finish;
118     }
119 
120     /* Block SIGTERM first, so that the event loop can handle it */
121     if (sigprocmask(SIG_BLOCK, &ss, nullptr) < 0)
122     {
123         r = -errno;
124         goto finish;
125     }
126 
127     /* Let's make use of the default handler and "floating" reference features
128      * of sd_event_add_signal() */
129     r = sd_event_add_signal(event, nullptr, SIGTERM, nullptr, nullptr);
130     if (r < 0)
131     {
132         goto finish;
133     }
134 
135     r = sd_event_add_signal(event, nullptr, SIGINT, nullptr, nullptr);
136     if (r < 0)
137     {
138         goto finish;
139     }
140 
141     if (sd_listen_fds(0) != 1)
142     {
143         log<level::ERR>("No or too many file descriptors received");
144         goto finish;
145     }
146 
147     fd = SD_LISTEN_FDS_START;
148 
149     r = sd_event_add_io(event, &source, fd, EPOLLIN, udp623Handler, nullptr);
150     if (r < 0)
151     {
152         goto finish;
153     }
154 
155     udpIPMI.reset(source);
156     source = nullptr;
157 
158     r = sd_event_loop(event);
159 
160 finish:
161     event = sd_event_unref(event);
162 
163     if (fd >= 0)
164     {
165         (void) close(fd);
166     }
167 
168     if (r < 0)
169     {
170         log<level::ERR>("Event Loop Failure:",
171                 entry("FAILURE=%s", strerror(-r)));
172     }
173 
174     return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
175 }
176 
177 void EventLoop::startHostConsole(const sol::CustomFD& fd)
178 {
179     int rc = 0;
180 
181     if((fd() == -1) || hostConsole.get())
182     {
183         throw std::runtime_error("Console descriptor already added");
184     }
185 
186     sd_event_source* source = nullptr;
187 
188     // Add the fd to the event loop for EPOLLIN
189     rc = sd_event_add_io(
190             event, &source, fd(), EPOLLIN, consoleInputHandler, nullptr);
191     if (rc < 0)
192     {
193         throw std::runtime_error("Failed to add socket descriptor");
194     }
195 
196     hostConsole.reset(source);
197     source=nullptr;
198 }
199 
200 void EventLoop::stopHostConsole()
201 {
202     int rc = 0;
203 
204     if (hostConsole.get())
205     {
206         // Disable the host console payload
207         rc = sd_event_source_set_enabled(hostConsole.get(), SD_EVENT_OFF);
208         if (rc < 0)
209         {
210             log<level::ERR>("Failed to disable the host console socket",
211                     entry("RC=%d", rc));
212             hostConsole.reset();
213             throw std::runtime_error("Failed to disable socket descriptor");
214         }
215 
216         hostConsole.reset();
217     }
218 }
219 
220 void EventLoop::switchTimer(uint8_t payloadInst,
221                             Timers type,
222                             bool status)
223 {
224     auto iter = payloadInfo.find(payloadInst);
225     if (iter == payloadInfo.end())
226     {
227         log<level::ERR>("SOL Payload instance not found",
228                 entry("payloadInst=%d", payloadInst));
229         throw std::runtime_error("SOL Payload instance not found");
230     }
231 
232     int rc = 0;
233     auto source = (std::get<0>(iter->second.at(type))).get();
234     auto interval = std::get<1>(iter->second.at(type));
235 
236     // Turn OFF the timer
237     if (!status)
238     {
239         rc = sd_event_source_set_enabled(source, SD_EVENT_OFF);
240         if (rc < 0)
241         {
242             log<level::ERR>("Failed to disable the timer", entry("RC=%d", rc));
243             throw std::runtime_error("Failed to disable timer");
244         }
245         return;
246     }
247 
248     // Turn ON the timer
249     uint64_t currentTime = 0;
250     rc = sd_event_now(event, CLOCK_MONOTONIC, &currentTime);
251     if (rc < 0)
252     {
253         log<level::ERR>("Failed to get the current timestamp",
254                 entry("RC=%d", rc));
255         throw std::runtime_error("Failed to get current timestamp");
256     }
257 
258     rc = sd_event_source_set_time(source, currentTime + interval.count());
259     if (rc < 0)
260     {
261         log<level::ERR>("sd_event_source_set_time function failed",
262                 entry("RC=%d", rc));
263         throw std::runtime_error("sd_event_source_set_time function failed");
264     }
265 
266     rc = sd_event_source_set_enabled(source, SD_EVENT_ONESHOT);
267     if (rc < 0)
268     {
269         log<level::ERR>("Failed to enable the timer", entry("RC=%d",rc));
270         throw std::runtime_error("Failed to enable timer");
271     }
272 }
273 
274 } // namespace eventloop
275