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 context.resendPayload(sol::Context::noClear); 153 } 154 else 155 { 156 context.retryCounter = context.maxRetryCount; 157 context.resendPayload(sol::Context::clear); 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 } 285 286 hostConsole.reset(); 287 } 288 } 289 290 void EventLoop::startSOLPayloadInstance(uint8_t payloadInst, 291 IntervalType accumulateInterval, 292 IntervalType retryInterval) 293 { 294 auto instance = payloadInst; 295 sd_event_source* accTimerSource = nullptr; 296 sd_event_source* retryTimerSource = nullptr; 297 int rc = 0; 298 uint64_t currentTime = 0; 299 300 rc = sd_event_now(event, CLOCK_MONOTONIC, ¤tTime); 301 if (rc < 0) 302 { 303 log<level::ERR>("Failed to get the current timestamp", 304 entry("RC=%d", rc)); 305 throw std::runtime_error("Failed to get current timestamp"); 306 } 307 308 // Create character accumulate timer 309 rc = sd_event_add_time(event, 310 &accTimerSource, 311 CLOCK_MONOTONIC, 312 currentTime + accumulateInterval.count(), 313 0, 314 charAccTimerHandler, 315 static_cast<void *>(&instance)); 316 if (rc < 0) 317 { 318 log<level::ERR>("Failed to setup the accumulate timer", 319 entry("RC = %d", rc)); 320 throw std::runtime_error("Failed to setup accumulate timer"); 321 } 322 323 // Create retry interval timer and add to the event loop 324 rc = sd_event_add_time(event, 325 &retryTimerSource, 326 CLOCK_MONOTONIC, 327 currentTime + retryInterval.count(), 328 0, 329 retryTimerHandler, 330 static_cast<void *>(&instance)); 331 if (rc < 0) 332 { 333 log<level::ERR>("Failed to setup the retry timer", 334 entry("RC = %d", rc)); 335 throw std::runtime_error("Failed to setup retry timer"); 336 } 337 338 // Enable the Character Accumulate Timer 339 rc = sd_event_source_set_enabled(accTimerSource, SD_EVENT_ONESHOT); 340 if (rc < 0) 341 { 342 log<level::ERR>("Failed to enable the accumulate timer", 343 entry("rc = %d", rc)); 344 throw std::runtime_error("Failed to enable accumulate timer"); 345 } 346 347 // Disable the Retry Interval Timer 348 rc = sd_event_source_set_enabled(retryTimerSource, SD_EVENT_OFF); 349 if (rc < 0) 350 { 351 log<level::ERR>("Failed to disable the retry timer", 352 entry("RC = %d", rc)); 353 throw std::runtime_error("Failed to disable retry timer"); 354 } 355 356 EventSource accEventSource(accTimerSource); 357 EventSource retryEventSource(retryTimerSource); 358 accTimerSource = nullptr; 359 retryTimerSource = nullptr; 360 361 TimerMap timer; 362 timer.emplace(Timers::ACCUMULATE, std::make_tuple(std::move(accEventSource), 363 accumulateInterval)); 364 timer.emplace(Timers::RETRY, std::make_tuple(std::move(retryEventSource), 365 retryInterval)); 366 payloadInfo.emplace(instance, std::move(timer)); 367 } 368 369 void EventLoop::stopSOLPayloadInstance(uint8_t payloadInst) 370 { 371 auto iter = payloadInfo.find(payloadInst); 372 if (iter == payloadInfo.end()) 373 { 374 log<level::ERR>("SOL Payload instance not found", 375 entry("payloadInst=%d", payloadInst)); 376 throw std::runtime_error("SOL payload instance not found"); 377 } 378 379 int rc = 0; 380 381 /* Destroy the character accumulate timer event source */ 382 rc = sd_event_source_set_enabled( 383 (std::get<0>(iter->second.at(Timers::ACCUMULATE))).get(), 384 SD_EVENT_OFF); 385 if (rc < 0) 386 { 387 log<level::ERR>("Failed to disable the character accumulate timer", 388 entry("RC=%d", rc)); 389 payloadInfo.erase(payloadInst); 390 throw std::runtime_error("Failed to disable accumulate timer"); 391 } 392 393 /* Destroy the retry interval timer event source */ 394 rc = sd_event_source_set_enabled( 395 (std::get<0>(iter->second.at(Timers::RETRY))).get(), 396 SD_EVENT_OFF); 397 if (rc < 0) 398 { 399 log<level::ERR>("Failed to disable the retry timer", 400 entry("RC=%d", rc)); 401 payloadInfo.erase(payloadInst); 402 throw std::runtime_error("Failed to disable retry timer"); 403 } 404 405 payloadInfo.erase(payloadInst); 406 } 407 408 void EventLoop::switchTimer(uint8_t payloadInst, 409 Timers type, 410 bool status) 411 { 412 auto iter = payloadInfo.find(payloadInst); 413 if (iter == payloadInfo.end()) 414 { 415 log<level::ERR>("SOL Payload instance not found", 416 entry("payloadInst=%d", payloadInst)); 417 throw std::runtime_error("SOL Payload instance not found"); 418 } 419 420 int rc = 0; 421 auto source = (std::get<0>(iter->second.at(type))).get(); 422 auto interval = std::get<1>(iter->second.at(type)); 423 424 // Turn OFF the timer 425 if (!status) 426 { 427 rc = sd_event_source_set_enabled(source, SD_EVENT_OFF); 428 if (rc < 0) 429 { 430 log<level::ERR>("Failed to disable the timer", entry("RC=%d", rc)); 431 throw std::runtime_error("Failed to disable timer"); 432 } 433 return; 434 } 435 436 // Turn ON the timer 437 uint64_t currentTime = 0; 438 rc = sd_event_now(event, CLOCK_MONOTONIC, ¤tTime); 439 if (rc < 0) 440 { 441 log<level::ERR>("Failed to get the current timestamp", 442 entry("RC=%d", rc)); 443 throw std::runtime_error("Failed to get current timestamp"); 444 } 445 446 rc = sd_event_source_set_time(source, currentTime + interval.count()); 447 if (rc < 0) 448 { 449 log<level::ERR>("sd_event_source_set_time function failed", 450 entry("RC=%d", rc)); 451 throw std::runtime_error("sd_event_source_set_time function failed"); 452 } 453 454 rc = sd_event_source_set_enabled(source, SD_EVENT_ONESHOT); 455 if (rc < 0) 456 { 457 log<level::ERR>("Failed to enable the timer", entry("RC=%d",rc)); 458 throw std::runtime_error("Failed to enable timer"); 459 } 460 } 461 462 } // namespace eventloop 463