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 static int charAccTimerHandler(sd_event_source* s, uint64_t usec, 101 void *userdata) 102 { 103 // The instance is hardcoded to 1, in the case of supporting multiple 104 // payload instances we would need to populate it from userdata 105 uint8_t instance = 1; 106 auto bufferSize = std::get<sol::Manager&>(singletonPool).dataBuffer.size(); 107 108 try 109 { 110 if(bufferSize > 0) 111 { 112 // Send the SOL payload 113 } 114 115 std::get<eventloop::EventLoop&>(singletonPool).switchTimer( 116 instance, Timers::ACCUMULATE, true); 117 } 118 catch (std::exception& e) 119 { 120 log<level::ERR>(e.what()); 121 } 122 123 return 0; 124 } 125 126 static int retryTimerHandler(sd_event_source* s, uint64_t usec, 127 void *userdata) 128 { 129 // The instance is hardcoded to 1, in the case of supporting multiple 130 // payload instances we would need to populate it from userdata 131 uint8_t instance = 1; 132 133 try 134 { 135 auto& context = std::get<sol::Manager&>(singletonPool).getContext 136 (instance); 137 138 if (context.retryCounter) 139 { 140 --context.retryCounter; 141 std::get<eventloop::EventLoop&>(singletonPool).switchTimer 142 (instance, Timers::RETRY, true); 143 // Resend the SOL payload 144 } 145 else 146 { 147 context.retryCounter = context.maxRetryCount; 148 // Resend the SOL payload 149 std::get<eventloop::EventLoop&>(singletonPool).switchTimer 150 (instance, Timers::RETRY, false); 151 std::get<eventloop::EventLoop&>(singletonPool).switchTimer 152 (instance, Timers::ACCUMULATE, true); 153 } 154 } 155 catch (std::exception& e) 156 { 157 log<level::ERR>(e.what()); 158 } 159 160 return 0; 161 } 162 163 int EventLoop::startEventLoop() 164 { 165 int fd = -1; 166 int r = 0; 167 sigset_t ss; 168 sd_event_source* source = nullptr; 169 170 r = sd_event_default(&event); 171 if (r < 0) 172 { 173 goto finish; 174 } 175 176 if (sigemptyset(&ss) < 0 || sigaddset(&ss, SIGTERM) < 0 || 177 sigaddset(&ss, SIGINT) < 0) 178 { 179 r = -errno; 180 goto finish; 181 } 182 183 /* Block SIGTERM first, so that the event loop can handle it */ 184 if (sigprocmask(SIG_BLOCK, &ss, nullptr) < 0) 185 { 186 r = -errno; 187 goto finish; 188 } 189 190 /* Let's make use of the default handler and "floating" reference features 191 * of sd_event_add_signal() */ 192 r = sd_event_add_signal(event, nullptr, SIGTERM, nullptr, nullptr); 193 if (r < 0) 194 { 195 goto finish; 196 } 197 198 r = sd_event_add_signal(event, nullptr, SIGINT, nullptr, nullptr); 199 if (r < 0) 200 { 201 goto finish; 202 } 203 204 if (sd_listen_fds(0) != 1) 205 { 206 log<level::ERR>("No or too many file descriptors received"); 207 goto finish; 208 } 209 210 fd = SD_LISTEN_FDS_START; 211 212 r = sd_event_add_io(event, &source, fd, EPOLLIN, udp623Handler, nullptr); 213 if (r < 0) 214 { 215 goto finish; 216 } 217 218 udpIPMI.reset(source); 219 source = nullptr; 220 221 r = sd_event_loop(event); 222 223 finish: 224 event = sd_event_unref(event); 225 226 if (fd >= 0) 227 { 228 (void) close(fd); 229 } 230 231 if (r < 0) 232 { 233 log<level::ERR>("Event Loop Failure:", 234 entry("FAILURE=%s", strerror(-r))); 235 } 236 237 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; 238 } 239 240 void EventLoop::startHostConsole(const sol::CustomFD& fd) 241 { 242 int rc = 0; 243 244 if((fd() == -1) || hostConsole.get()) 245 { 246 throw std::runtime_error("Console descriptor already added"); 247 } 248 249 sd_event_source* source = nullptr; 250 251 // Add the fd to the event loop for EPOLLIN 252 rc = sd_event_add_io( 253 event, &source, fd(), EPOLLIN, consoleInputHandler, nullptr); 254 if (rc < 0) 255 { 256 throw std::runtime_error("Failed to add socket descriptor"); 257 } 258 259 hostConsole.reset(source); 260 source=nullptr; 261 } 262 263 void EventLoop::stopHostConsole() 264 { 265 int rc = 0; 266 267 if (hostConsole.get()) 268 { 269 // Disable the host console payload 270 rc = sd_event_source_set_enabled(hostConsole.get(), SD_EVENT_OFF); 271 if (rc < 0) 272 { 273 log<level::ERR>("Failed to disable the host console socket", 274 entry("RC=%d", rc)); 275 hostConsole.reset(); 276 throw std::runtime_error("Failed to disable socket descriptor"); 277 } 278 279 hostConsole.reset(); 280 } 281 } 282 283 void EventLoop::startSOLPayloadInstance(uint8_t payloadInst, 284 IntervalType accumulateInterval, 285 IntervalType retryInterval) 286 { 287 auto instance = payloadInst; 288 sd_event_source* accTimerSource = nullptr; 289 sd_event_source* retryTimerSource = nullptr; 290 int rc = 0; 291 uint64_t currentTime = 0; 292 293 rc = sd_event_now(event, CLOCK_MONOTONIC, ¤tTime); 294 if (rc < 0) 295 { 296 log<level::ERR>("Failed to get the current timestamp", 297 entry("RC=%d", rc)); 298 throw std::runtime_error("Failed to get current timestamp"); 299 } 300 301 // Create character accumulate timer 302 rc = sd_event_add_time(event, 303 &accTimerSource, 304 CLOCK_MONOTONIC, 305 currentTime + accumulateInterval.count(), 306 0, 307 charAccTimerHandler, 308 static_cast<void *>(&instance)); 309 if (rc < 0) 310 { 311 log<level::ERR>("Failed to setup the accumulate timer", 312 entry("RC = %d", rc)); 313 throw std::runtime_error("Failed to setup accumulate timer"); 314 } 315 316 // Create retry interval timer and add to the event loop 317 rc = sd_event_add_time(event, 318 &retryTimerSource, 319 CLOCK_MONOTONIC, 320 currentTime + retryInterval.count(), 321 0, 322 retryTimerHandler, 323 static_cast<void *>(&instance)); 324 if (rc < 0) 325 { 326 log<level::ERR>("Failed to setup the retry timer", 327 entry("RC = %d", rc)); 328 throw std::runtime_error("Failed to setup retry timer"); 329 } 330 331 // Enable the Character Accumulate Timer 332 rc = sd_event_source_set_enabled(accTimerSource, SD_EVENT_ONESHOT); 333 if (rc < 0) 334 { 335 log<level::ERR>("Failed to enable the accumulate timer", 336 entry("rc = %d", rc)); 337 throw std::runtime_error("Failed to enable accumulate timer"); 338 } 339 340 // Disable the Retry Interval Timer 341 rc = sd_event_source_set_enabled(retryTimerSource, SD_EVENT_OFF); 342 if (rc < 0) 343 { 344 log<level::ERR>("Failed to disable the retry timer", 345 entry("RC = %d", rc)); 346 throw std::runtime_error("Failed to disable retry timer"); 347 } 348 349 EventSource accEventSource(accTimerSource); 350 EventSource retryEventSource(retryTimerSource); 351 accTimerSource = nullptr; 352 retryTimerSource = nullptr; 353 354 TimerMap timer; 355 timer.emplace(Timers::ACCUMULATE, std::make_tuple(std::move(accEventSource), 356 accumulateInterval)); 357 timer.emplace(Timers::RETRY, std::make_tuple(std::move(retryEventSource), 358 retryInterval)); 359 payloadInfo.emplace(instance, std::move(timer)); 360 } 361 362 void EventLoop::switchTimer(uint8_t payloadInst, 363 Timers type, 364 bool status) 365 { 366 auto iter = payloadInfo.find(payloadInst); 367 if (iter == payloadInfo.end()) 368 { 369 log<level::ERR>("SOL Payload instance not found", 370 entry("payloadInst=%d", payloadInst)); 371 throw std::runtime_error("SOL Payload instance not found"); 372 } 373 374 int rc = 0; 375 auto source = (std::get<0>(iter->second.at(type))).get(); 376 auto interval = std::get<1>(iter->second.at(type)); 377 378 // Turn OFF the timer 379 if (!status) 380 { 381 rc = sd_event_source_set_enabled(source, SD_EVENT_OFF); 382 if (rc < 0) 383 { 384 log<level::ERR>("Failed to disable the timer", entry("RC=%d", rc)); 385 throw std::runtime_error("Failed to disable timer"); 386 } 387 return; 388 } 389 390 // Turn ON the timer 391 uint64_t currentTime = 0; 392 rc = sd_event_now(event, CLOCK_MONOTONIC, ¤tTime); 393 if (rc < 0) 394 { 395 log<level::ERR>("Failed to get the current timestamp", 396 entry("RC=%d", rc)); 397 throw std::runtime_error("Failed to get current timestamp"); 398 } 399 400 rc = sd_event_source_set_time(source, currentTime + interval.count()); 401 if (rc < 0) 402 { 403 log<level::ERR>("sd_event_source_set_time function failed", 404 entry("RC=%d", rc)); 405 throw std::runtime_error("sd_event_source_set_time function failed"); 406 } 407 408 rc = sd_event_source_set_enabled(source, SD_EVENT_ONESHOT); 409 if (rc < 0) 410 { 411 log<level::ERR>("Failed to enable the timer", entry("RC=%d",rc)); 412 throw std::runtime_error("Failed to enable timer"); 413 } 414 } 415 416 } // namespace eventloop 417