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