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