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