/************************************************************************
 *
 *  epoll.c: Linux epoll 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 <adrian@creative.net.au>
 *  Copyright (C) 2002 ircd-ratbox development team
 *  Copyright (C) 2002 Aaron Sethman <androsyn@ratbox.org>
 *
 * 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_epoll.c,v 1.1 2004/02/24 15:02:21 tr-ircd Exp $
 */

#include "struct.h"
#include "common.h"
#include "sys.h"
#include "h.h"
#include "fd.h"
#include "s_bsd.h"
#include "s_conf.h"
#include "listener.h"
#include "numeric.h"
#include "packet.h"
#include "resnew.h"
#include "s_auth.h"

#ifdef USE_EPOLL

#ifndef HAVE_EPOLL_CTL /* bah..glibc doesn't support epoll yet.. */
#include <sys/epoll.h>
#include <sys/syscall.h>

_syscall1(int, epoll_create, int, maxfds);
_syscall4(int, epoll_ctl, int, epfd, int, op, int, fd, struct epoll_event *, events);
_syscall4(int, epoll_wait, int, epfd, struct epoll_event *, pevents,
                 int, maxevents, int, timeout);

#endif /* HAVE_EPOLL_CTL */


static int ep;			/* epoll file descriptor */
static int hep;
static struct epoll_event *pfd;
static int pfd_size;

/* 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)
{
    pfd_size = getdtablesize();
    ep = epoll_create(pfd_size);
    hep = epoll_create(pfd_size);
    pfd = MyMalloc(sizeof(struct epoll_event) * pfd_size);
    if (ep < 0) {
	logevent_call(LogSys.epoll_netio);
	exit(115);		/* Whee! */
    }
}

/*
 * 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)
{
    struct epoll_event ep_event;
    fde_t *F = &fd_table[fd];
    int old_flags = F->pflags;
    int op = -1;

    assert(fd >= 0);
    assert(F->flags.open);

    /* Update the list, even though we're not using it .. */
    F->list = list;
    if (type & COMM_SELECT_READ) {
	if (handler != NULL)
	    F->pflags |= EPOLLIN;
	else
	    F->pflags &= ~EPOLLIN;

	F->read_handler = handler;
	F->read_data = client_data;
    }
    if (type & COMM_SELECT_WRITE) {
	if (handler != NULL)
	    F->pflags |= EPOLLOUT;
	else
	    F->pflags &= ~EPOLLOUT;

	F->write_handler = handler;
	F->write_data = client_data;
    }
    if (timeout)
	F->timeout = timeofday + (timeout / 1000);
    if (old_flags == 0 && F->pflags == 0)
	return;
    else if (F->pflags <= 0)
	op = EPOLL_CTL_DEL;
    else if (old_flags == 0 && F->pflags > 0)
	op = EPOLL_CTL_ADD;
    else if (F->pflags != old_flags)
	op = EPOLL_CTL_MOD;

    if (op == -1)
	return;

    ep_event.events = F->pflags;
    ep_event.data.ptr = F;

    if (epoll_ctl(ep, op, fd, &ep_event) != 0) {
	logevent_call(LogSys.epoll_ctl, strerror(errno));
	// abort();
    }

}

/*
 * 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.
 */

/*
 * comm_select
 *
 * 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, i, flags, old_flags, op;
    struct epoll_event ep_event;
    void *data;

    int q;

    if (list == FDLIST_IRCD)
	q = ep;
    else if (list == FDLIST_HTTPD)
	q = hep;
    else
	q = ep;

    num = epoll_wait(q, pfd, pfd_size, delay);
    recheck_clock(NULL);
    if (num < 0 && !ignoreErrno(errno)) {
	return COMM_ERROR;
    }
    if (num == 0)
	return COMM_OK;
    *callbacks += num;

    if (num == 0)
	return COMM_OK;
    for (i = 0; i < num; i++) {
    PF *hdl;
    fde_t *F = pfd[i].data.ptr;
	old_flags = F->pflags;
	if (pfd[i].events & (EPOLLIN | EPOLLHUP | EPOLLERR)) {
	    hdl = F->read_handler;
	    data = F->read_data;
	    F->read_handler = NULL;
	    F->read_data = NULL;
	    if (hdl) {
		hdl(F->fd, data);
	    }
	}

	if (F->flags.open == 0)
	    continue;
	if (pfd[i].events & (EPOLLOUT | EPOLLHUP | EPOLLERR)) {
	    hdl = F->write_handler;
	    data = F->write_data;
	    F->write_handler = NULL;
	    F->write_data = NULL;

	    if (hdl) {
		hdl(F->fd, data);
	    }
	}

	if (F->flags.open == 0)
	    continue;

	flags = 0;

	if (F->read_handler != NULL)
	    flags |= EPOLLIN;
	if (F->write_handler != NULL)
	    flags |= EPOLLOUT;

	if (old_flags != flags) {
	    if (flags == 0)
		op = EPOLL_CTL_DEL;
	    else
		op = EPOLL_CTL_MOD;
	    F->pflags = ep_event.events = flags;
	    ep_event.data.ptr = F;
	    if(epoll_ctl(ep, op, F->fd, &ep_event) != 0) {
		logevent_call(LogSys.epoll_ctl, strerror(errno));
	    }		
	}

    }
    return COMM_OK;
}

#endif


syntax highlighted by Code2HTML, v. 0.9.1