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