/* * ircd-ratbox: A slightly useful ircd. * s_bsd_poll.c: POSIX poll() compatible network routines. * * Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center * Copyright (C) 1996-2002 Hybrid Development Team * Copyright (C) 2001 Adrian Chadd * Copyright (C) 2002-2005 ircd-ratbox development team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA * */ #include #include #include #include #ifdef HAVE_SYS_POLL_H #include #endif /* I hate linux -- adrian */ #ifndef POLLRDNORM #define POLLRDNORM POLLIN #endif #ifndef POLLWRNORM #define POLLWRNORM POLLOUT #endif struct _pollfd_list { struct pollfd *pollfds; int maxindex; /* highest FD number */ int allocated; /* number of pollfds allocated */ }; typedef struct _pollfd_list pollfd_list_t; static pollfd_list_t pollfd_list; int rb_setup_fd_poll(rb_fde_t *F __attribute__((unused))) { return 0; } /* * rb_init_netio * * This is a needed exported function which will be called to initialise * the network loop code. */ int rb_init_netio_poll(void) { int fd; pollfd_list.pollfds = rb_malloc(rb_getmaxconnect() * (sizeof(struct pollfd))); pollfd_list.allocated = rb_getmaxconnect(); for(fd = 0; fd < rb_getmaxconnect(); fd++) { pollfd_list.pollfds[fd].fd = -1; } pollfd_list.maxindex = 0; return 0; } static void resize_pollarray(int fd) { if(rb_unlikely(fd >= pollfd_list.allocated)) { int x, old_value = pollfd_list.allocated; pollfd_list.allocated += 1024; pollfd_list.pollfds = rb_realloc(pollfd_list.pollfds, pollfd_list.allocated * (sizeof(struct pollfd))); memset(&pollfd_list.pollfds[old_value + 1], 0, sizeof(struct pollfd) * 1024); for(x = old_value + 1; x < pollfd_list.allocated; x++) { pollfd_list.pollfds[x].fd = -1; } } } /* * rb_setselect * * This is a needed exported function which will be called to register * and deregister interest in a pending IO state for a given FD. */ void rb_setselect_poll(rb_fde_t *F, unsigned int type, PF * handler, void *client_data) { if(F == NULL) return; if(type & RB_SELECT_READ) { F->read_handler = handler; F->read_data = client_data; if(handler != NULL) F->pflags |= POLLRDNORM; else F->pflags &= ~POLLRDNORM; } if(type & RB_SELECT_WRITE) { F->write_handler = handler; F->write_data = client_data; if(handler != NULL) F->pflags |= POLLWRNORM; else F->pflags &= ~POLLWRNORM; } resize_pollarray(F->fd); if(F->pflags <= 0) { pollfd_list.pollfds[F->fd].events = 0; pollfd_list.pollfds[F->fd].fd = -1; if(F->fd == pollfd_list.maxindex) { while(pollfd_list.maxindex >= 0 && pollfd_list.pollfds[pollfd_list.maxindex].fd == -1) pollfd_list.maxindex--; } } else { pollfd_list.pollfds[F->fd].events = F->pflags; pollfd_list.pollfds[F->fd].fd = F->fd; if(F->fd > pollfd_list.maxindex) pollfd_list.maxindex = F->fd; } } /* int rb_select(unsigned long delay) * Input: The maximum time to delay. * Output: Returns -1 on error, 0 on success. * Side-effects: Deregisters future interest in IO and calls the handlers * if an event occurs for an FD. * Comments: Check all connections for new connections and input data * that is to be processed. Also check for connections with data queued * and whether we can write it out. * Called to do the new-style IO, courtesy of squid (like most of this * new IO code). This routine handles the stuff we've hidden in * rb_setselect and fd_table[] and calls callbacks for IO ready * events. */ int rb_select_poll(long delay) { int num; int fd; int ci; PF *hdl; void *data; struct pollfd *pfd; int revents; num = poll(pollfd_list.pollfds, pollfd_list.maxindex + 1, delay); rb_set_time(); if(num < 0) { if(!rb_ignore_errno(errno)) return RB_OK; else return RB_ERROR; } if(num == 0) return RB_OK; /* XXX we *could* optimise by falling out after doing num fds ... */ for(ci = 0; ci < pollfd_list.maxindex + 1; ci++) { rb_fde_t *F; pfd = &pollfd_list.pollfds[ci]; revents = pfd->revents; fd = pfd->fd; if(revents == 0 || fd == -1) continue; F = rb_find_fd(fd); if(F == NULL) continue; if(revents & (POLLRDNORM | POLLIN | POLLHUP | POLLERR)) { hdl = F->read_handler; data = F->read_data; F->read_handler = NULL; F->read_data = NULL; if(hdl) hdl(F, data); } if(IsFDOpen(F) && (revents & (POLLWRNORM | POLLOUT | POLLHUP | POLLERR))) { hdl = F->write_handler; data = F->write_data; F->write_handler = NULL; F->write_data = NULL; if(hdl) hdl(F, data); } if(F->read_handler == NULL) rb_setselect_poll(F, RB_SELECT_READ, NULL, NULL); if(F->write_handler == NULL) rb_setselect_poll(F, RB_SELECT_WRITE, NULL, NULL); } return 0; }