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