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