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