/* * ircd-ratbox: A slightly useful ircd. * sigio.c: Linux Realtime SIGIO 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 Aaron Sethman * Copyright (C) 2002 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 * */ #ifndef _GNU_SOURCE #define _GNU_SOURCE 1 /* Needed for F_SETSIG */ #endif #include #include #include #include #include /* Yes this needs to be before the ifdef */ #if defined(HAVE_SYS_POLL_H) && (HAVE_POLL) && (F_SETSIG) #define USING_SIGIO #endif #ifdef USING_SIGIO #include #include #if defined(USE_TIMER_CREATE) #define SIGIO_SCHED_EVENT 1 #endif #define RTSIGIO SIGRTMIN #define RTSIGTIM (SIGRTMIN+1) struct _pollfd_list { struct pollfd *pollfds; int maxindex; /* highest FD number */ int allocated; }; typedef struct _pollfd_list pollfd_list_t; pollfd_list_t pollfd_list; static int can_do_event = 0; static int sigio_is_screwed = 0; /* We overflowed our sigio queue */ static sigset_t our_sigset; /* * rb_init_netio * * This is a needed exported function which will be called to initialise * the network loop code. */ int rb_init_netio_sigio(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; sigio_is_screwed = 1; /* Start off with poll first.. */ sigemptyset(&our_sigset); sigaddset(&our_sigset, RTSIGIO); sigaddset(&our_sigset, SIGIO); #ifdef SIGIO_SCHED_EVENT sigaddset(&our_sigset, RTSIGTIM); #endif sigprocmask(SIG_BLOCK, &our_sigset, NULL); return 0; } static inline 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; } } } /* * void setup_sigio_fd(int fd) * * Input: File descriptor * Output: None * Side Effect: Sets the FD up for SIGIO */ int rb_setup_fd_sigio(rb_fde_t *F) { int flags = 0; int fd = F->fd; flags = fcntl(fd, F_GETFL, 0); if(flags == -1) return 0; /* if set async, clear it so we can reset it in the kernel :/ */ if(flags & O_ASYNC) { flags &= ~O_ASYNC; fcntl(fd, F_SETFL, flags); } flags |= O_ASYNC | O_NONBLOCK; if(fcntl(fd, F_SETFL, flags) == -1) return 0; if(fcntl(fd, F_SETSIG, RTSIGIO) == -1) return 0; if(fcntl(fd, F_SETOWN, getpid()) == -1) return 0; return 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_sigio(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(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_sigio(long delay) { int num = 0; int revents = 0; int sig; int fd; int ci; PF *hdl; rb_fde_t *F; void *data; struct siginfo si; struct timespec timeout; if(rb_sigio_supports_event() || delay >= 0) { timeout.tv_sec = (delay / 1000); timeout.tv_nsec = (delay % 1000) * 1000000; } for(;;) { if(!sigio_is_screwed) { if(can_do_event || delay < 0) { sig = sigwaitinfo(&our_sigset, &si); } else sig = sigtimedwait(&our_sigset, &si, &timeout); if(sig > 0) { if(sig == SIGIO) { rb_lib_log ("Kernel RT Signal queue overflowed. Is ulimit -i too small(or perhaps /proc/sys/kernel/rtsig-max on old kernels)"); sigio_is_screwed = 1; break; } #ifdef SIGIO_SCHED_EVENT if(sig == RTSIGTIM && can_do_event) { struct ev_entry *ev = (struct ev_entry *)si.si_ptr; if(ev == NULL) continue; rb_run_event(ev); continue; } #endif fd = si.si_fd; pollfd_list.pollfds[fd].revents |= si.si_band; revents = pollfd_list.pollfds[fd].revents; num++; 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(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); } } else break; } else break; } if(!sigio_is_screwed) /* We don't need to proceed */ { rb_set_time(); return 0; } signal(RTSIGIO, SIG_IGN); signal(RTSIGIO, SIG_DFL); sigio_is_screwed = 0; 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++) { if(((revents = pollfd_list.pollfds[ci].revents) == 0) || (pollfd_list.pollfds[ci].fd) == -1) continue; fd = pollfd_list.pollfds[ci].fd; 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_sigio(F, RB_SELECT_READ, NULL, NULL); if(F->write_handler == NULL) rb_setselect_sigio(F, RB_SELECT_WRITE, NULL, NULL); } return 0; } #if defined(SIGIO_SCHED_EVENT) void rb_sigio_init_event(void) { rb_sigio_supports_event(); } int rb_sigio_supports_event(void) { timer_t timer; struct sigevent ev; if(can_do_event == 1) return 1; if(can_do_event == -1) return 0; ev.sigev_signo = SIGVTALRM; ev.sigev_notify = SIGEV_SIGNAL; if(timer_create(CLOCK_REALTIME, &ev, &timer) != 0) { can_do_event = -1; return 0; } timer_delete(timer); can_do_event = 1; return 1; } int rb_sigio_sched_event(struct ev_entry *event, int when) { timer_t *id; struct sigevent ev; struct itimerspec ts; if(can_do_event <= 0) return 0; memset(&ev, 0, sizeof(&ev)); event->comm_ptr = rb_malloc(sizeof(timer_t)); id = event->comm_ptr; ev.sigev_notify = SIGEV_SIGNAL; ev.sigev_signo = RTSIGTIM; ev.sigev_value.sival_ptr = event; if(timer_create(CLOCK_REALTIME, &ev, id) < 0) { rb_lib_log("timer_create: %s\n", strerror(errno)); return 0; } memset(&ts, 0, sizeof(ts)); ts.it_value.tv_sec = when; ts.it_value.tv_nsec = 0; if(event->frequency != 0) ts.it_interval = ts.it_value; if(timer_settime(*id, 0, &ts, NULL) < 0) { rb_lib_log("timer_settime: %s\n", strerror(errno)); return 0; } return 1; } void rb_sigio_unsched_event(struct ev_entry *event) { if(can_do_event <= 0) return; timer_delete(*((timer_t *) event->comm_ptr)); rb_free(event->comm_ptr); event->comm_ptr = NULL; } #endif /* SIGIO_SCHED_EVENT */ #else int rb_init_netio_sigio(void) { return ENOSYS; } void rb_setselect_sigio(rb_fde_t *F, unsigned int type, PF * handler, void *client_data) { errno = ENOSYS; return; } int rb_select_sigio(long delay) { errno = ENOSYS; return -1; } int rb_setup_fd_sigio(rb_fde_t *F) { errno = ENOSYS; return -1; } #endif #if !defined(USING_SIGIO) || !defined(SIGIO_SCHED_EVENT) void rb_sigio_init_event(void) { return; } int rb_sigio_sched_event(struct ev_entry *event, int when) { errno = ENOSYS; return -1; } void rb_sigio_unsched_event(struct ev_entry *event) { return; } int rb_sigio_supports_event(void) { errno = ENOSYS; return 0; } #endif /* !USING_SIGIO || !SIGIO_SCHED_EVENT */