/************************************************************************
*
* s_bsd_sigio.c - code implementing a sigio IO loop
* Copyright 2001 Aaron Sethman <androsyn@ratbox.org>
* based upon: s_bsd_poll.c
* By Adrian Chadd <adrian@creative.net.au>
*
* Based upon:
*
* IRC - Internet Relay Chat, server/s_bsd.c
*
* Copyright (C) 2000-2003 TR-IRCD Development
*
* Copyright (C) 1990 Jarkko Oikarinen and
* University of Oulu, Co Center
*
* 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: s_bsd_sigio.c,v 1.3 2003/06/14 13:55:52 tr-ircd Exp $
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE 1 /* Needed for F_SETSIG */
#endif
#include "struct.h"
#include "common.h"
#include "sys.h"
#include "h.h"
#include "fd.h"
#include "s_bsd.h"
#include "listener.h"
#include "numeric.h"
#include "packet.h"
#include "resnew.h"
#include "s_auth.h"
#ifdef USE_SIGIO
#ifndef POLLRDNORM
#define POLLRDNORM POLLIN
#endif
#ifndef POLLWRNORM
#define POLLWRNORM POLLOUT
#endif
#ifndef F_SETSIG
#define F_SETSIG 10 /* Set number of signal to be sent. */
#endif
#ifndef O_ASYNC
#define O_ASYNC 020000
#endif
static int sigio_signal;
static int sigio_is_screwed = 0; /* We overflowed our sigio queue */
static sigset_t our_sigset;
struct _pollfd_list {
struct pollfd pollfds[MAXCONNECTIONS];
int maxindex; /* highest FD number */
};
typedef struct _pollfd_list pollfd_list_t;
pollfd_list_t pollfd_list;
pollfd_list_t httpfd_list;
static void poll_update_pollfds(int, fdlist_t, short, PF *);
static void mask_our_signal(int s)
{
sigemptyset(&our_sigset);
sigaddset(&our_sigset, s);
sigprocmask(SIG_BLOCK, &our_sigset, NULL);
}
void do_sigio(int s)
{
sigio_is_screwed = 1;
}
void setup_sigio_fd(int fd)
{
int flags;
fcntl(fd, F_SETOWN, getpid());
fcntl(fd, F_SETSIG, sigio_signal);
fcntl(fd, F_GETFL, &flags);
flags |= O_ASYNC | O_NONBLOCK;
fcntl(fd, F_SETFL, flags);
}
/*
* find a spare slot in the fd list. We can optimise this out later!
* -- adrian
*/
static inline int poll_findslot(pollfd_list_t *list)
{
int i;
for (i = 0; i < MAXCONNECTIONS; i++) {
if (list->pollfds[i].fd == -1) {
/* MATCH!!#$*&$ */
return i;
}
}
assert(1 == 0);
/* NOTREACHED */
return -1;
}
/*
* set and clear entries in the pollfds[] array.
*/
static void poll_update_pollfds(int fd, fdlist_t list, short event, PF * handler)
{
fde_t *F = &fd_table[fd];
int comm_index;
pollfd_list_t *fd_list;
if (list == FDLIST_IRCD)
fd_list = &pollfd_list;
else if (list == FDLIST_HTTPD)
fd_list = &httpfd_list;
else
fd_list = &pollfd_list;
if (F->comm_index < 0)
F->comm_index = poll_findslot(fd_list);
comm_index = F->comm_index;
/* Update the events */
if (handler) {
F->list = list;
fd_list->pollfds[comm_index].events |= event;
fd_list->pollfds[comm_index].fd = fd;
/* update maxindex here */
if (comm_index > fd_list->maxindex)
fd_list->maxindex = comm_index;
} else {
if (comm_index >= 0) {
fd_list->pollfds[comm_index].events &= ~event;
if (fd_list->pollfds[comm_index].events == 0) {
fd_list->pollfds[comm_index].fd = -1;
fd_list->pollfds[comm_index].revents = 0;
F->comm_index = -1;
F->list = FDLIST_NONE;
/* update pollfd_list.maxindex here */
if (comm_index == fd_list->maxindex)
while (fd_list->maxindex >= 0 && fd_list->pollfds[fd_list->maxindex].fd == -1)
fd_list->maxindex--;
}
}
}
}
/* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
/* Public functions */
/*
* init_netio
*
* This is a needed exported function which will be called to initialise
* the network loop code.
*/
void init_netio(void)
{
int fd;
sigio_signal = SIGRTMIN;
memset(&pollfd_list, 0, sizeof(pollfd_list));
memset(&httpfd_list, 0, sizeof(httpfd_list));
for (fd = 0; fd < MAXCONNECTIONS; fd++) {
pollfd_list.pollfds[fd].fd = -1;
httpfd_list.pollfds[fd].fd = -1;
}
pollfd_list.maxindex = 0;
httpfd_list.maxindex = 0;
mask_our_signal(sigio_signal);
}
/*
* comm_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
comm_setselect(int fd, fdlist_t list, unsigned int type, PF * handler,
void *client_data, time_t timeout)
{
fde_t *F = &fd_table[fd];
assert(fd >= 0);
assert(F->flags.open);
F->list = list;
if (type & COMM_SELECT_READ) {
F->read_handler = handler;
F->read_data = client_data;
poll_update_pollfds(fd, list, POLLRDNORM, handler);
}
if (type & COMM_SELECT_WRITE) {
F->write_handler = handler;
F->write_data = client_data;
poll_update_pollfds(fd, list, POLLWRNORM | POLLOUT, handler);
}
if (timeout)
F->timeout = timeofday + (timeout / 1000);
}
/* int comm_select_fdlist(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 of squid (like most of this
* new IO code). This routine handles the stuff we've hidden in
* comm_setselect and fd_table[] and calls callbacks for IO ready
* events.
*/
int comm_select(unsigned long delay, int *callbacks, fdlist_t list)
{
int num = 0;
static int loop_count = 0;
int sig;
int fd;
int ci;
PF *hdl;
struct siginfo si;
struct timespec timeout;
timeout.tv_sec = 0;
timeout.tv_nsec = delay * 1000 * 1000;
pollfd_list_t *fd_list;
if (list == FDLIST_IRCD)
fd_list = &pollfd_list;
else if (list == FDLIST_HTTPD)
fd_list = &httpfd_list;
else
return 0;
for (;;) {
if (!sigio_is_screwed && ++loop_count <= 5) {
if ((sig = sigtimedwait(&our_sigset, &si, &timeout)) > 0) {
if (sig == SIGIO) {
sigio_is_screwed = 1;
break;
}
fd = si.si_fd;
fd_list->pollfds[fd].revents |= si.si_band;
num++;
} else {
break;
}
} else {
if (sigio_is_screwed) {
signal(sigio_signal, SIG_IGN);
signal(sigio_signal, SIG_DFL);
sigio_is_screwed = 0;
}
num = poll(fd_list->pollfds, fd_list->maxindex + 1, 0);
loop_count = 0;
if (num >= 0)
break;
if (ignoreErrno(errno))
continue;
/* error! */
recheck_clock(NULL);
return -1;
/* NOTREACHED */
}
}
/* update current time again, eww.. */
recheck_clock(NULL);
*callbacks += num;
if (num == 0)
return 0;
/* XXX we *could* optimise by falling out after doing num fds ... */
for (ci = 0; ci < fd_list->maxindex + 1; ci++) {
fde_t *F;
int revents;
if (((revents = fd_list->pollfds[ci].revents) == 0) ||
(fd_list->pollfds[ci].fd) == -1)
continue;
fd = fd_list->pollfds[ci].fd;
F = &fd_table[fd];
if (revents & (POLLRDNORM | POLLIN | POLLHUP | POLLERR)) {
hdl = F->read_handler;
F->read_handler = NULL;
poll_update_pollfds(fd, F->list, POLLRDNORM, NULL);
if (hdl)
hdl(fd, F->read_data);
}
if (revents & (POLLWRNORM | POLLOUT | POLLHUP | POLLERR)) {
hdl = F->write_handler;
F->write_handler = NULL;
poll_update_pollfds(fd, F->list, POLLWRNORM, NULL);
if (hdl)
hdl(fd, F->write_data);
}
}
return 0;
}
#endif
syntax highlighted by Code2HTML, v. 0.9.1