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