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