unrealircd- supernets unrealircd source & configuration |
git clone git://git.acid.vegas/unrealircd.git |
Log | Files | Refs | Archive | README | LICENSE |
dispatch.c (15576B)
1 /* 2 * UnrealIRCd, src/dispatch.c 3 * Copyright (c) 2012 William Pitcock <nenolod@dereferenced.org> 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 1, or (at your option) 8 * any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 18 */ 19 20 #include "unrealircd.h" 21 22 /* Some specials here, for this file.. */ 23 24 /* Do we even support this, poll on Windows? */ 25 #ifdef BACKEND_POLL 26 #ifndef _WIN32 27 # include <poll.h> 28 #else 29 # define poll WSAPoll 30 # define POLLRDHUP POLLHUP 31 #endif 32 #endif 33 34 #ifdef _WIN32 35 #include <WinSock2.h> 36 #endif 37 #ifndef _WIN32 38 #include <sys/file.h> 39 #include <sys/ioctl.h> 40 #endif 41 42 /* Not sure if this is suitable for production, 43 * but let's turn it on for U6 development. 44 */ 45 //#define DETECT_HIGH_CPU 46 47 /*************************************************************************************** 48 * Backend-independent functions. fd_setselect() and friends * 49 ***************************************************************************************/ 50 void fd_setselect(int fd, int flags, IOCallbackFunc iocb, void *data) 51 { 52 FDEntry *fde; 53 int changed = 0; 54 #if 0 55 unreal_log(ULOG_DEBUG, "io", "IO_DEBUG_FD_SETSELECT", NULL, 56 "fd_setselect(): fd $fd flags $fd_flags function $function_pointer", 57 log_data_integer("fd", fd), 58 log_data_integer("fd_flags", flags), 59 log_data_integer("function_pointer", (long long)iocb)); 60 #endif 61 if ((fd < 0) || (fd >= MAXCONNECTIONS)) 62 { 63 unreal_log(ULOG_ERROR, "io", "BUG_FD_SETSELECT_OUT_OF_RANGE", NULL, 64 "[BUG] trying to modify fd $fd in fd table, but MAXCONNECTIONS is $maxconnections", 65 log_data_integer("fd", fd), 66 log_data_integer("maxconnections", MAXCONNECTIONS)); 67 #ifdef DEBUGMODE 68 abort(); 69 #endif 70 return; 71 } 72 73 fde = &fd_table[fd]; 74 fde->data = data; 75 76 if (flags & FD_SELECT_READ) 77 { 78 if (fde->read_callback != iocb) 79 { 80 fde->read_callback = iocb; 81 changed = 1; 82 } 83 } 84 if (flags & FD_SELECT_WRITE) 85 { 86 if (fde->write_callback != iocb) 87 { 88 fde->write_callback = iocb; 89 changed = 1; 90 } 91 } 92 93 // This is efficient, but.. there are places which do two fd_setselect(), 94 // it would be nice if we can merge this into one syscall.. 95 if (changed) 96 fd_refresh(fd); 97 } 98 99 /*************************************************************************************** 100 * select() backend. * 101 ***************************************************************************************/ 102 #ifdef BACKEND_SELECT 103 104 #ifndef _WIN32 105 # include <sys/select.h> 106 #endif 107 108 static int highest_fd = -1; 109 static fd_set read_fds, write_fds; 110 111 void fd_refresh(int fd) 112 { 113 FDEntry *fde = &fd_table[fd]; 114 unsigned int flags = 0; 115 116 if (fde->read_callback) 117 { 118 flags |= FD_SELECT_READ; 119 120 FD_SET(fd, &read_fds); 121 } 122 else 123 FD_CLR(fd, &read_fds); 124 125 if (fde->write_callback) 126 { 127 flags |= FD_SELECT_WRITE; 128 129 FD_SET(fd, &write_fds); 130 } 131 else 132 FD_CLR(fd, &write_fds); 133 134 if (flags && highest_fd < fd) 135 highest_fd = fd; 136 137 while (highest_fd > 0 && 138 !(FD_ISSET(highest_fd, &read_fds) || FD_ISSET(highest_fd, &write_fds))) 139 highest_fd--; 140 141 fde->backend_flags = flags; 142 } 143 144 void fd_debug(fd_set *f, int highest, char *name) 145 { 146 int i; 147 for (i = 0; i < highest; i++) 148 { 149 if (FD_ISSET(i, f)) 150 { 151 /* check if fd 'i' is valid... */ 152 //if (fcntl(i, F_GETFL) < 0) 153 int nonb = 1; 154 if (ioctlsocket(i, FIONBIO, &nonb) < 0) 155 { 156 unreal_log(ULOG_ERROR, "io", "FD_DEBUG", NULL, 157 "[BUG] fd_debug: fd $fd is invalid!!!", 158 log_data_integer("fd", i)); 159 } 160 } 161 } 162 } 163 void fd_select(int delay) 164 { 165 struct timeval to; 166 int num, fd; 167 fd_set work_read_fds; 168 fd_set work_write_fds; 169 #ifdef _WIN32 170 fd_set work_except_fds; /* only needed on windows as it may indicate a failed connect() */ 171 #endif 172 173 /* copy the FD sets so that our master sets are untouched */ 174 memcpy(&work_read_fds, &read_fds, sizeof(fd_set)); 175 memcpy(&work_write_fds, &write_fds, sizeof(fd_set)); 176 #ifdef _WIN32 177 memcpy(&work_except_fds, &write_fds, sizeof(fd_set)); 178 #endif 179 180 memset(&to, 0, sizeof(to)); 181 to.tv_sec = delay / 1000; 182 to.tv_usec = (delay % 1000) * 1000; 183 184 #ifdef _WIN32 185 num = select(highest_fd + 1, &work_read_fds, &work_write_fds, &work_except_fds, &to); 186 #else 187 num = select(highest_fd + 1, &work_read_fds, &work_write_fds, NULL, &to); 188 #endif 189 if (num < 0) 190 { 191 unreal_log(ULOG_FATAL, "io", "SELECT_ERROR", NULL, 192 "select() returned error ($socket_error) -- SERIOUS TROUBLE!", 193 log_data_socket_error(-1)); 194 /* DEBUG the actual problem: */ 195 memcpy(&work_read_fds, &read_fds, sizeof(fd_set)); 196 memcpy(&work_write_fds, &write_fds, sizeof(fd_set)); 197 fd_debug(&work_read_fds, highest_fd+1, "read"); 198 fd_debug(&work_write_fds, highest_fd+1, "write"); 199 #ifdef _WIN32 200 Sleep(500); 201 #endif 202 } 203 204 if (num <= 0) 205 return; 206 207 for (fd = 0; fd <= highest_fd && num > 0; fd++) 208 { 209 FDEntry *fde; 210 IOCallbackFunc iocb; 211 int evflags = 0; 212 213 fde = &fd_table[fd]; 214 if (!fde->is_open) 215 continue; 216 217 if (FD_ISSET(fd, &work_read_fds)) 218 evflags |= FD_SELECT_READ; 219 220 if (FD_ISSET(fd, &work_write_fds)) 221 evflags |= FD_SELECT_WRITE; 222 223 #ifdef _WIN32 224 /* Exception may happen due to failed connect. Translate to write event, like on *NIX. */ 225 if (FD_ISSET(fd, &work_except_fds)) 226 evflags |= FD_SELECT_WRITE; 227 #endif 228 229 if (!evflags) 230 continue; 231 232 if (evflags & FD_SELECT_READ) 233 { 234 iocb = fde->read_callback; 235 236 if (iocb != NULL) 237 iocb(fd, evflags, fde->data); 238 } 239 240 if (evflags & FD_SELECT_WRITE) 241 { 242 iocb = fde->write_callback; 243 244 if (iocb != NULL) 245 iocb(fd, evflags, fde->data); 246 } 247 248 num--; 249 } 250 } 251 252 void fd_fork() 253 { 254 } 255 256 #endif 257 258 /*************************************************************************************** 259 * kqueue() backend. * 260 ***************************************************************************************/ 261 #ifdef BACKEND_KQUEUE 262 263 #include <sys/event.h> 264 265 static int kqueue_fd = -1; 266 static struct kevent kqueue_events[MAXCONNECTIONS * 2]; 267 static struct kevent kqueue_prepared[MAXCONNECTIONS * 2]; 268 static char kqueue_enabled[MAXCONNECTIONS * 2]; 269 270 void fd_fork() 271 { 272 kqueue_fd = kqueue(); 273 int p; 274 275 for (p=0; p < MAXCONNECTIONS * 2; ++p) 276 { 277 if (kqueue_enabled[p]) 278 { 279 if (kevent(kqueue_fd, &kqueue_prepared[p], 1, NULL, 0, &(const struct timespec){ .tv_sec = 0, .tv_nsec = 0}) != 0) 280 { 281 if (ERRNO == P_EWOULDBLOCK || ERRNO == P_EAGAIN) 282 continue; 283 284 #ifdef DEBUGMODE 285 unreal_log(ULOG_ERROR, "io", "KEVENT_FAILED", NULL, 286 "[io] fd_fork(): kevent returned error: $system_error", 287 log_data_string("system_error", strerror(errno))); 288 #endif 289 } 290 } 291 } 292 } 293 294 void fd_refresh(int fd) 295 { 296 FDEntry *fde = &fd_table[fd]; 297 298 if (kqueue_fd == -1) 299 { 300 kqueue_fd = kqueue(); 301 memset(kqueue_enabled,0,MAXCONNECTIONS*2); 302 } 303 304 kqueue_enabled[fd] = 0; 305 kqueue_enabled[fd+MAXCONNECTIONS] = 0; 306 307 if (fde->read_callback != NULL || fde->backend_flags & EVFILT_READ) 308 { 309 EV_SET(&kqueue_prepared[fd], (uintptr_t) fd, (short) EVFILT_READ, fde->read_callback != NULL ? EV_ADD : EV_DELETE, 0, 0, fde); 310 if (kevent(kqueue_fd, &kqueue_prepared[fd], 1, NULL, 0, &(const struct timespec){ .tv_sec = 0, .tv_nsec = 0}) != 0) 311 { 312 #ifdef DEBUGMODE 313 if (ERRNO != P_EWOULDBLOCK && ERRNO != P_EAGAIN) 314 { 315 int save_err = errno; 316 unreal_log(ULOG_ERROR, "io", "KEVENT_FAILED_REFRESH", NULL, 317 "fd_refresh(): kevent returned error for fd $fd ($fd_action) ($callback): $system_error", 318 log_data_string("system_error", strerror(save_err)), 319 log_data_integer("fd", fd), 320 log_data_string("fd_action", (fde->read_callback ? "add" : "delete")), 321 log_data_string("callback", "read_callback")); 322 } 323 #endif 324 } 325 } 326 327 if (fde->write_callback != NULL || fde->backend_flags & EVFILT_WRITE) 328 { 329 EV_SET(&kqueue_prepared[fd+MAXCONNECTIONS], (uintptr_t) fd, (short) EVFILT_WRITE, fde->write_callback != NULL ? EV_ADD : EV_DELETE, 0, 0, fde); 330 if (kevent(kqueue_fd, &kqueue_prepared[fd+MAXCONNECTIONS], 1, NULL, 0, &(const struct timespec){ .tv_sec = 0, .tv_nsec = 0}) != 0) 331 { 332 #ifdef DEBUGMODE 333 if (ERRNO != P_EWOULDBLOCK && ERRNO != P_EAGAIN && fde->write_callback) 334 { 335 int save_err = errno; 336 unreal_log(ULOG_ERROR, "io", "KEVENT_FAILED_REFRESH", NULL, 337 "[io] fd_refresh(): kevent returned error for fd $fd ($fd_action) ($callback): $system_error", 338 log_data_string("system_error", strerror(save_err)), 339 log_data_integer("fd", fd), 340 log_data_string("fd_action", "add"), 341 log_data_string("callback", "write_callback")); 342 } 343 #endif 344 } 345 } 346 347 fde->backend_flags = 0; 348 349 if (fde->read_callback != NULL) 350 { 351 fde->backend_flags |= EVFILT_READ; 352 kqueue_enabled[fd] = 1; 353 354 } 355 356 if (fde->write_callback != NULL) 357 { 358 fde->backend_flags |= EVFILT_WRITE; 359 kqueue_enabled[fd+MAXCONNECTIONS] = 1; 360 } 361 } 362 363 void fd_select(int delay) 364 { 365 struct timespec ts; 366 int num, p, revents, fd; 367 struct kevent *ke; 368 369 if (kqueue_fd == -1) 370 { 371 kqueue_fd = kqueue(); 372 memset(kqueue_enabled,0,MAXCONNECTIONS*2); 373 } 374 375 memset(&ts, 0, sizeof(ts)); 376 ts.tv_sec = delay / 1000; 377 ts.tv_nsec = delay % 1000 * 1000000; 378 379 num = kevent(kqueue_fd, NULL, 0, kqueue_events, MAXCONNECTIONS * 2, &ts); 380 if (num <= 0) 381 return; 382 383 for (p = 0; p < num; p++) 384 { 385 FDEntry *fde; 386 IOCallbackFunc iocb; 387 int evflags = 0; 388 389 ke = &kqueue_events[p]; 390 fd = ke->ident; 391 revents = ke->filter; 392 fde = ke->udata; 393 394 if (revents == EVFILT_READ) 395 { 396 iocb = fde->read_callback; 397 398 if (iocb != NULL) 399 iocb(fd, FD_SELECT_READ, fde->data); 400 } 401 402 if (revents == EVFILT_WRITE) 403 { 404 iocb = fde->write_callback; 405 406 if (iocb != NULL) 407 iocb(fd, FD_SELECT_WRITE, fde->data); 408 } 409 } 410 } 411 #endif 412 413 /*************************************************************************************** 414 * epoll() backend. * 415 ***************************************************************************************/ 416 #ifdef BACKEND_EPOLL 417 418 #include <sys/epoll.h> 419 420 static int epoll_fd = -1; 421 static struct epoll_event epfds[MAXCONNECTIONS + 1]; 422 423 void fd_refresh(int fd) 424 { 425 struct epoll_event ep_event; 426 FDEntry *fde = &fd_table[fd]; 427 unsigned int pflags = 0; 428 int op = -1; 429 430 if (epoll_fd == -1) 431 epoll_fd = epoll_create(MAXCONNECTIONS); 432 433 if (fde->read_callback) 434 pflags |= EPOLLIN; 435 436 if (fde->write_callback) 437 pflags |= EPOLLOUT; 438 439 if (pflags == 0 && fde->backend_flags == 0) 440 return; 441 else if (pflags == 0) 442 op = EPOLL_CTL_DEL; 443 else if (fde->backend_flags == 0 && pflags != 0) 444 op = EPOLL_CTL_ADD; 445 else if (fde->backend_flags != pflags) 446 op = EPOLL_CTL_MOD; 447 448 if (op == -1) 449 return; 450 451 memset(&ep_event, 0, sizeof(ep_event)); 452 ep_event.events = pflags; 453 ep_event.data.ptr = fde; 454 455 if (epoll_ctl(epoll_fd, op, fd, &ep_event) != 0) 456 { 457 int save_errno = errno; 458 if ((save_errno == P_EWOULDBLOCK) || (save_errno == P_EAGAIN)) 459 return; 460 461 unreal_log(ULOG_ERROR, "io", "EPOLL_CTL_FAILED", NULL, 462 "[io] fd_refresh(): epoll_ctl returned error for fd $fd ($fd_description): $system_error", 463 log_data_string("system_error", strerror(save_errno)), 464 log_data_integer("fd", fd), 465 log_data_string("fd_description", fde->desc)); 466 return; 467 } 468 469 fde->backend_flags = pflags; 470 } 471 472 void fd_select(int delay) 473 { 474 int num, p, revents, fd; 475 struct epoll_event *epfd; 476 #ifdef DETECT_HIGH_CPU 477 int read_callbacks = 0, write_callbacks = 0; 478 struct timeval oldt, t; 479 long long tdiff; 480 #endif 481 if (epoll_fd == -1) 482 epoll_fd = epoll_create(MAXCONNECTIONS); 483 484 num = epoll_wait(epoll_fd, epfds, MAXCONNECTIONS, delay); 485 if (num <= 0) 486 return; 487 488 #ifdef DETECT_HIGH_CPU 489 gettimeofday(&oldt, NULL); 490 #endif 491 492 for (p = 0; p < num; p++) 493 { 494 FDEntry *fde; 495 IOCallbackFunc iocb; 496 int evflags = 0; 497 498 epfd = &epfds[p]; 499 500 revents = epfd->events; 501 if (revents == 0) 502 continue; 503 504 fde = epfd->data.ptr; 505 fd = fde->fd; 506 507 if (revents & (EPOLLIN | EPOLLHUP | EPOLLERR)) 508 evflags |= FD_SELECT_READ; 509 510 if (revents & (EPOLLOUT | EPOLLHUP | EPOLLERR)) 511 evflags |= FD_SELECT_WRITE; 512 513 if (evflags & FD_SELECT_READ) 514 { 515 iocb = fde->read_callback; 516 517 if (iocb != NULL) 518 iocb(fd, evflags, fde->data); 519 520 #ifdef DETECT_HIGH_CPU 521 read_callbacks++; 522 #endif 523 } 524 525 if (evflags & FD_SELECT_WRITE) 526 { 527 iocb = fde->write_callback; 528 529 if (iocb != NULL) 530 iocb(fd, evflags, fde->data); 531 532 #ifdef DETECT_HIGH_CPU 533 write_callbacks++; 534 #endif 535 } 536 #if 0 537 if (((read_callbacks + write_callbacks) % 100) == 0) 538 { 539 /* every 100 events.. set the internal clock so we don't screw up under extreme load */ 540 timeofday = time(NULL); 541 } 542 #endif 543 } 544 545 #ifdef DETECT_HIGH_CPU 546 gettimeofday(&t, NULL); 547 tdiff = ((t.tv_sec - oldt.tv_sec) * 1000000) + (t.tv_usec - oldt.tv_usec); 548 549 if (tdiff > 1000000) 550 { 551 unreal_log(ULOG_WARNING, "io", "HIGH_LOAD", NULL, 552 "HIGH CPU LOAD! fd_select() took $time_msec msec " 553 "(read: $num_read_callbacks, write: $num_write_callbacks)", 554 log_data_integer("time_msec", tdiff/1000), 555 log_data_integer("num_read_callbacks", read_callbacks), 556 log_data_integer("num_write_callbacks", write_callbacks)); 557 } 558 #endif 559 } 560 561 562 void fd_fork() 563 { 564 } 565 566 #endif 567 568 /*************************************************************************************** 569 * Poll() backend. * 570 ***************************************************************************************/ 571 #ifdef BACKEND_POLL 572 573 #ifndef POLLRDNORM 574 # define POLLRDNORM POLLIN 575 #endif 576 #ifndef POLLWRNORM 577 # define POLLWRNORM POLLOUT 578 #endif 579 580 static struct pollfd pollfds[FD_SETSIZE]; 581 static nfds_t nfds = 0; 582 583 void fd_refresh(int fd) 584 { 585 FDEntry *fde = &fd_table[fd]; 586 unsigned int pflags = 0; 587 unsigned int i; 588 589 if (fde->read_callback) 590 pflags |= (POLLRDNORM | POLLIN); 591 592 if (fde->write_callback) 593 pflags |= (POLLWRNORM | POLLOUT); 594 595 pollfds[fde->fd].events = pflags; 596 pollfds[fde->fd].fd = pflags ? fde->fd : -1; 597 598 /* tighten maximum pollfd */ 599 if (pflags && nfds < fde->fd) 600 nfds = fde->fd; 601 602 while (nfds > 0 && pollfds[nfds].fd == -1) 603 nfds--; 604 605 fde->backend_flags = pflags; 606 } 607 608 void fd_select(int delay) 609 { 610 int num, p, revents, fd; 611 struct pollfd *pfd; 612 613 num = poll(pollfds, nfds + 1, delay); 614 if (num <= 0) 615 return; 616 617 for (p = 0; p < (nfds + 1); p++) 618 { 619 FDEntry *fde; 620 IOCallbackFunc iocb; 621 int evflags = 0; 622 623 pfd = &pollfds[p]; 624 625 revents = pfd->revents; 626 fd = pfd->fd; 627 if (revents == 0 || fd == -1) 628 continue; 629 630 fde = &fd_table[fd]; 631 632 if (revents & (POLLRDNORM | POLLIN | POLLHUP | POLLERR)) 633 evflags |= FD_SELECT_READ; 634 635 if (revents & (POLLWRNORM | POLLOUT | POLLHUP | POLLERR)) 636 evflags |= FD_SELECT_WRITE; 637 638 if (evflags & FD_SELECT_READ) 639 { 640 iocb = fde->read_callback; 641 642 if (iocb != NULL) 643 iocb(fd, evflags, fde->data); 644 } 645 646 if (evflags & FD_SELECT_WRITE) 647 { 648 iocb = fde->write_callback; 649 if (iocb != NULL) 650 iocb(fd, evflags, fde->data); 651 } 652 } 653 } 654 655 656 void fd_fork() 657 { 658 } 659 660 #endif