Commit Diff


commit - ebbf92898e749da9341ebe3b7e71b96362161043
commit + 7eca418465cf3c74c7aee6dfc9a2ab9ba7fbfcfe
blob - /dev/null
blob + efeba72f5a2c8558cc648311168bd5acb9b92feb (mode 644)
--- /dev/null
+++ src/ngircd/io.c
@@ -0,0 +1,611 @@
+/*
+ * 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.
+ * Please read the file COPYING, README and AUTHORS for more information.
+ *
+ * I/O abstraction interface.
+ * Copyright (c) 2005 Florian Westphal (westphal@foo.fh-furtwangen.de)
+ *
+ */
+
+#include "portab.h"
+
+static char UNUSED id[] = "$Id: io.c,v 1.1 2005/07/07 18:38:35 fw Exp $";
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "array.h"
+#include "io.h"
+#include "log.h"
+
+
+typedef struct {
+ void (*callback)(int, short);
+ int fd;
+ short what;
+} io_event;
+
+#define INIT_IOEVENT    { NULL, -1, 0, NULL }
+#define IO_ERROR        4
+
+#ifdef HAVE_EPOLL_CREATE
+#define IO_USE_EPOLL    1
+#else
+# ifdef HAVE_KQUEUE
+#define IO_USE_KQUEUE   1
+# else
+#define IO_USE_SELECT   1
+#endif
+#endif
+
+
+#ifdef IO_USE_EPOLL
+#include <sys/epoll.h>
+
+static int io_masterfd;
+static bool io_event_new_epoll(int fd, short what);
+static bool io_event_change_epoll(int fd, short what);
+static int io_dispatch_epoll(struct timeval *tv);
+#endif
+
+#ifdef IO_USE_KQUEUE
+#include <sys/types.h>
+#include <sys/event.h>
+static array io_evcache;
+static int io_masterfd;
+
+static int io_dispatch_kqueue(struct timeval *tv);
+static bool io_event_add_kqueue(int, short);
+#endif
+
+#ifdef IO_USE_SELECT
+#include "defines.h"	/* for conn.h */
+#include "conn.h"	/* for CONN_IDX (needed by resolve.h) */
+#include "resolve.h"	/* for RES_STAT (needed by conf.h) */
+#include "conf.h"	/* for Conf_MaxConnections */
+
+static fd_set readers;
+static fd_set writers;
+static int select_maxfd;		/* the select() interface sucks badly */
+static int io_dispatch_select(struct timeval *tv);
+#endif
+
+static array io_events;
+
+static void io_docallback PARAMS((int fd, short what));
+
+static io_event *
+io_event_get(int fd)
+{
+	io_event *i;
+	assert(fd >= 0);
+	i = (io_event *) array_get(&io_events, sizeof(io_event), fd);
+	assert(i);
+
+	if (!i) {
+#ifdef DEBUG
+		Log(LOG_DEBUG, "io_event_add(): EMPTY FOR fd %d", fd);
+#endif
+		return NULL;
+	}
+	return i;
+}
+
+
+bool
+io_library_init(unsigned int eventsize)
+{
+#ifdef IO_USE_EPOLL
+	int ecreate_hint = (int)eventsize;
+	if (ecreate_hint <= 0)
+		ecreate_hint = 128;
+#endif
+
+#ifdef IO_USE_SELECT
+#ifdef FD_SETSIZE
+	if (eventsize >= FD_SETSIZE)
+		eventsize = FD_SETSIZE - 1;
+#endif
+#endif
+	if (eventsize && !array_alloc(&io_events, sizeof(io_event), eventsize))
+		eventsize = 0;
+
+#ifdef IO_USE_EPOLL
+	io_masterfd = epoll_create(ecreate_hint);
+	Log(LOG_INFO,
+	    "io subsystem: using epoll (hint size %d), initial io_event maxfd: %u, io_masterfd %d",
+	    ecreate_hint, eventsize, io_masterfd);
+	return io_masterfd >= 0;
+#endif
+#ifdef IO_USE_SELECT
+	Log(LOG_INFO, "io subsystem: using select, initial io_event maxfd: %u",
+	    eventsize);
+	FD_ZERO(&readers);
+	FD_ZERO(&writers);
+#ifdef FD_SETSIZE
+	if (Conf_MaxConnections >= FD_SETSIZE) {
+		Log(LOG_WARNING,
+		    "Conf_MaxConnections (%d) exceeds limit (%u), changed Conf_MaxConnections to %u",
+		    Conf_MaxConnections, FD_SETSIZE, FD_SETSIZE - 1);
+
+		Conf_MaxConnections = FD_SETSIZE - 1;
+	}
+#else
+	Log(LOG_WARNING,
+	    "FD_SETSIZE undefined, don't know how many descriptors select() can handle on your platform");
+#endif
+	return true;
+#endif
+#ifdef IO_USE_KQUEUE
+	io_masterfd = kqueue();
+
+	Log(LOG_INFO,
+	    "io subsystem: using kqueue, initial io_event maxfd: %u, io_masterfd %d",
+	    eventsize, io_masterfd);
+	return io_masterfd >= 0;
+#endif
+}
+
+
+bool
+io_library_shutdown(void)
+{
+	unsigned int len = array_length(&io_events, sizeof(io_event));
+
+	while (len--) {
+		if (NULL == io_event_get(len))
+			continue;
+	}
+#ifndef IO_USE_SELECT
+	close(io_masterfd);	/* kqueue, epoll */
+	io_masterfd = -1;
+#else
+	FD_ZERO(&readers);
+	FD_ZERO(&writers);
+#endif
+#ifdef IO_USE_KQUEUE
+	array_free(&io_evcache);
+#endif
+	return true;
+}
+
+
+bool
+io_event_setcb(int fd, void (*cbfunc) (int, short))
+{
+	io_event *i = io_event_get(fd);
+	if (!i)
+		return false;
+
+	i->callback = cbfunc;
+	return true;
+}
+
+
+bool
+io_event_create(int fd, short what, void (*cbfunc) (int, short))
+{
+	io_event *i;
+
+	assert(fd >= 0);
+
+#ifdef IO_USE_SELECT
+#ifdef FD_SETSIZE
+	if (fd >= FD_SETSIZE) {
+		Log(LOG_ERR,
+		    "fd %d exceeds FD_SETSIZE (%u) (select can't handle more file descriptors)",
+		    fd, FD_SETSIZE);
+		return false;
+	}
+#endif				/* FD_SETSIZE */
+#endif				/* IO_USE_SELECT */
+
+	i = (io_event *) array_alloc(&io_events, sizeof(io_event), fd);
+	if (!i) {
+		Log(LOG_WARNING,
+		    "array_alloc failed: could not allocate space for %d io_event structures",
+		    fd);
+		return false;
+	}
+
+	i->fd = fd;
+	i->callback = cbfunc;
+#ifdef IO_USE_EPOLL
+	i->what = what;
+	return io_event_new_epoll(fd, what);
+#endif
+#ifdef IO_USE_KQUEUE
+	i->what = what;
+	return io_event_add_kqueue(fd, what);
+#endif
+#ifdef IO_USE_SELECT
+	i->what = 0;
+	return io_event_add(fd, what);
+#endif
+}
+
+
+#ifdef IO_USE_EPOLL
+static bool
+io_event_new_epoll(int fd, short what)
+{
+	struct epoll_event ev = { 0, {0} };
+	ev.data.fd = fd;
+
+	if (what & IO_WANTREAD)
+		ev.events = EPOLLIN | EPOLLPRI;
+	if (what & IO_WANTWRITE)
+		ev.events |= EPOLLOUT;
+
+	return epoll_ctl(io_masterfd, EPOLL_CTL_ADD, fd, &ev) == 0;
+}
+
+
+static bool
+io_event_change_epoll(int fd, short what)
+{
+	struct epoll_event ev = { 0, {0} };
+	ev.data.fd = fd;
+
+	if (what & IO_WANTREAD)
+		ev.events = EPOLLIN | EPOLLPRI;
+	if (what & IO_WANTWRITE)
+		ev.events |= EPOLLOUT;
+
+	return epoll_ctl(io_masterfd, EPOLL_CTL_MOD, fd, &ev) == 0;
+}
+#endif
+
+#ifdef IO_USE_KQUEUE
+static bool
+io_event_kqueue_commit_cache(void)
+{
+	struct kevent *events;
+	bool ret;
+	int len = (int) array_length(&io_evcache, sizeof (struct kevent));
+ 
+	if (!len) /* nothing to do */
+		return true;
+
+	assert(len>0);
+
+	if (len < 0) {
+		array_free(&io_evcache);
+		return false;
+	}
+
+	events = array_start(&io_evcache);
+
+	assert(events);
+
+	ret = kevent(io_masterfd, events, len, NULL, 0, NULL) == 0;
+	if (ret)
+		array_trunc(&io_evcache);
+	return ret;
+}
+
+
+static bool
+io_event_add_kqueue(int fd, short what)
+{
+	struct kevent kev;
+	short filter = 0;
+	unsigned int len = array_length(&io_evcache, sizeof kev);
+	bool ret;
+
+	if (what & IO_WANTREAD)
+		filter = EVFILT_READ;
+
+	if (what & IO_WANTWRITE)
+		filter |= EVFILT_WRITE;
+
+	if (len >= 100) {
+		ret = io_event_kqueue_commit_cache();
+		if (ret)
+			array_trunc(&io_evcache);
+	}
+
+	EV_SET(&kev, fd, filter, EV_ADD | EV_ENABLE, 0, 0, NULL);
+	return array_catb(&io_evcache, (char*) &kev, sizeof (kev));
+}
+#endif
+
+
+bool
+io_event_add(int fd, short what)
+{
+	io_event *i = io_event_get(fd);
+
+	assert(i);
+
+	if (!i)
+		return false;
+	if (i->what == what)
+		return true;
+#ifdef DEBUG
+	Log(LOG_DEBUG, "io_event_add(): fd %d (arg: %d), what %d.", i->fd, fd,
+	    what);
+#endif
+
+	i->what |= what;
+
+#ifdef IO_USE_EPOLL
+	return io_event_change_epoll(fd, i->what);
+#endif
+
+#ifdef IO_USE_KQUEUE
+	return io_event_add_kqueue(fd, what);
+#endif
+
+#ifdef IO_USE_SELECT
+	if (fd > select_maxfd)
+		select_maxfd = fd;
+
+	if (what & IO_WANTREAD)
+		FD_SET(fd, &readers);
+	if (what & IO_WANTWRITE)
+		FD_SET(fd, &writers);
+
+	return true;
+#endif
+}
+
+
+bool
+io_setnonblock(int fd)
+{
+	int flags = fcntl(fd, F_GETFL);
+	if (flags == -1)
+		return false;
+
+#ifndef O_NONBLOCK
+#define O_NONBLOCK O_NDELAY
+#endif
+	flags |= O_NONBLOCK;
+
+	return fcntl(fd, F_SETFL, flags) == 0;
+}
+
+
+bool
+io_close(int fd)
+{
+	io_event *i = io_event_get(fd);
+	if (i) {
+		memset(i, 0, sizeof(io_event));
+		i->fd = -1;
+	}
+#ifdef IO_USE_SELECT
+	FD_CLR(fd, &writers);
+	FD_CLR(fd, &readers);
+
+	if (fd == select_maxfd)
+		select_maxfd--;
+#endif
+#ifdef IO_USE_KQUEUE
+	if (array_length(&io_evcache, sizeof (struct kevent)))	/* pending data in cache? */
+		io_event_kqueue_commit_cache();
+#endif
+	return close(fd) == 0;	/* both epoll an kqueue will remove fd from all sets automatically */
+}
+
+
+bool
+io_event_del(int fd, short what)
+{
+#ifdef IO_USE_KQUEUE
+	struct kevent kev;
+	short filter = 0;
+#endif
+	io_event *i = io_event_get(fd);
+#ifdef DEBUG
+	Log(LOG_DEBUG, "io_event_del(): trying to delete eventtype %d on fd %d",
+	    what, fd);
+#endif
+	assert(i);
+	if (!i)
+		return true;
+
+	i->what &= ~what;
+
+#ifdef IO_USE_EPOLL
+	return io_event_change_epoll(fd, i->what);
+#endif
+
+#ifdef IO_USE_KQUEUE
+	if (what & IO_WANTREAD)
+		filter = EVFILT_READ;
+
+	if (what & IO_WANTWRITE)
+		filter |= EVFILT_WRITE;
+
+	EV_SET(&kev, fd, filter, EV_DELETE, 0, 0, NULL);
+	return kevent(io_masterfd, &kev, 1, NULL, 0, NULL) == 0;
+#endif
+
+#ifdef IO_USE_SELECT
+	if (what & IO_WANTWRITE)
+		FD_CLR(i->fd, &writers);
+
+	if (what & IO_WANTREAD)
+		FD_CLR(i->fd, &readers);
+
+	return true;
+#endif
+}
+
+
+#ifdef IO_USE_SELECT
+static int
+io_dispatch_select(struct timeval *tv)
+{
+	fd_set readers_tmp = readers;
+	fd_set writers_tmp = writers;
+	short what;
+	int ret, i;
+	int fds_ready;
+	ret = select(select_maxfd + 1, &readers_tmp, &writers_tmp, NULL, tv);
+	if (ret <= 0)
+		return ret;
+
+	fds_ready = ret;
+
+	for (i = 0; i <= select_maxfd; i++) {
+		what = 0;
+		if (FD_ISSET(i, &readers_tmp)) {
+			what = IO_WANTREAD;
+			fds_ready--;
+		}
+
+		if (FD_ISSET(i, &writers_tmp)) {
+			what |= IO_WANTWRITE;
+			fds_ready--;
+		}
+		if (what)
+			io_docallback(i, what);
+		if (fds_ready <= 0)
+			break;
+	}
+
+	return ret;
+}
+#endif
+
+
+#ifdef IO_USE_EPOLL
+static int
+io_dispatch_epoll(struct timeval *tv)
+{
+	time_t sec = tv->tv_sec * 1000;
+	int i, total = 0, ret, timeout = tv->tv_usec + sec;
+	struct epoll_event epoll_ev[100];
+	short type;
+
+	if (timeout < 0)
+		timeout = 1000;
+
+	do {
+		ret = epoll_wait(io_masterfd, epoll_ev, 100, timeout);
+		total += ret;
+		if (ret <= 0)
+			return total;
+
+		for (i = 0; i < ret; i++) {
+			type = 0;
+			if (epoll_ev[i].events & (EPOLLERR | EPOLLHUP))
+				type = IO_ERROR;
+
+			if (epoll_ev[i].events & (EPOLLIN | EPOLLPRI))
+				type |= IO_WANTREAD;
+
+			if (epoll_ev[i].events & EPOLLOUT)
+				type |= IO_WANTWRITE;
+
+			io_docallback(epoll_ev[i].data.fd, type);
+		}
+
+		timeout = 0;
+	} while (ret == 100);
+
+	return total;
+}
+#endif
+
+
+#ifdef IO_USE_KQUEUE
+static int
+io_dispatch_kqueue(struct timeval *tv)
+{
+	int i, total = 0, ret;
+	struct kevent kev[100];
+	struct kevents *newevents;
+	struct timespec ts;
+	int newevents_len;
+	short type;
+	ts.tv_sec = tv->tv_sec;
+	ts.tv_nsec = tv->tv_usec * 1000;
+	
+	do {
+		newevents_len = array_length(&io_evcache, sizeof (struct kevent));
+		newevents = (newevents_len > 0) ? array_start(&io_evcache) : NULL;
+		assert(newevents_len >= 0);
+		if (newevents_len < 0)
+			newevents_len = 0;
+#ifdef DEBUG
+		if (newevents_len)
+			assert(newevents);
+#endif
+
+		ret = kevent(io_masterfd, newevents, newevents_len, kev, 100, &ts);
+		if ((newevents_len>0) && ret != -1)
+			array_trunc(&io_evcache);
+
+		total += ret;
+		if (ret <= 0)
+			return total;
+
+		for (i = 0; i < ret; i++) {
+			type = 0;
+			if (kev[i].flags & EV_EOF)
+				type = IO_ERROR;
+
+			if (kev[i].filter & EV_ERROR)
+				type = IO_ERROR;
+
+			if (kev[i].filter & EVFILT_READ)
+				type |= IO_WANTREAD;
+
+			if (kev[i].filter & EVFILT_WRITE)
+				type |= IO_WANTWRITE;
+
+			io_docallback(kev[i].ident, type);
+		}
+
+		ts.tv_sec = 0;
+		ts.tv_nsec = 0;
+
+	} while (ret == 100);
+
+	return total;
+}
+#endif
+
+
+int
+io_dispatch(struct timeval *tv)
+{
+#ifdef IO_USE_SELECT
+	return io_dispatch_select(tv);
+#endif
+#ifdef IO_USE_KQUEUE
+	return io_dispatch_kqueue(tv);
+#endif
+#ifdef IO_USE_EPOLL
+	return io_dispatch_epoll(tv);
+#endif
+}
+
+
+/* call the callback function inside the struct matching fd */
+static void
+io_docallback(int fd, short what)
+{
+	io_event *i;
+#ifdef DEBUG
+	Log(LOG_DEBUG, "doing callback for fd %d, what %d", fd, what);
+#endif
+	i = io_event_get(fd);
+	assert(i);
+
+	if (i->callback)	/* callback might be 0 if previous callback function called io_close on this fd */
+		i->callback(fd, (what & IO_ERROR) ? i->what : what);
+	/* if error indicator is set, we return the event(s) the app asked for */
+}
blob - /dev/null
blob + 3890bcc7df8111ead63749b1c1d844e1dde22e7c (mode 644)
--- /dev/null
+++ src/ngircd/io.h
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ * Please read the file COPYING, README and AUTHORS for more information.
+ *
+ * I/O abstraction interface header
+ *
+ * $Id: io.h,v 1.1 2005/07/07 18:38:35 fw Exp $
+ */
+
+#ifndef io_H_inclucded
+#define io_H_included
+
+#include "portab.h"
+#include <sys/time.h>
+
+#define IO_WANTREAD	1
+#define IO_WANTWRITE	2
+
+/* init library.  sets up epoll/kqueue descriptors and tries to allocate space for ioevlen file descriptors.
+   ioevlen is just the _initial_ size, not a limit. */
+bool io_library_init PARAMS((unsigned int ioevlen));
+
+/* shutdown and free all internal data structures */
+bool io_library_shutdown PARAMS((void));
+
+/* add fd to internal set, enable readability check, set callback */
+bool io_event_create PARAMS((int fd, short what, void (*cbfunc)(int, short)));
+
+/* change callback function associated with fd */
+bool io_event_setcb PARAMS((int fd, void (*cbfunc)(int, short)));
+
+/* watch fd for event of type what */
+bool io_event_add PARAMS((int fd, short what));
+
+/* do not watch fd for event of type what */
+bool io_event_del PARAMS((int fd, short what));
+
+/* remove fd from watchlist, close() fd.  */
+bool io_close PARAMS((int fd));
+
+/* set O_NONBLOCK */
+bool io_setnonblock PARAMS((int fd));
+
+/* watch fds for activity */
+int io_dispatch PARAMS((struct timeval *tv));
+
+#endif /* io_H_included */