X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?p=ngircd-alex.git;a=blobdiff_plain;f=src%2Fngircd%2Fio.c;h=037c4afca2af7c0cf9662058ee2a18d3a92b12eb;hp=34066b2b3958928910e588cf6ba879b99caeb722;hb=f547981188a28844068e864dc1ed955ff173d216;hpb=7f44a2ad1c8e6c28b6d7f384a3af9da12e22fcab diff --git a/src/ngircd/io.c b/src/ngircd/io.c index 34066b2b..037c4afc 100644 --- a/src/ngircd/io.c +++ b/src/ngircd/io.c @@ -1,34 +1,36 @@ /* + * ngIRCd -- The Next Generation IRC Daemon + * Copyright (c)2005-2006 Florian Westphal (westphal@foo.fh-furtwangen.de) + * Copyright (c)2006-2014 Alexander Barton (alex@barton.de) and Contributors. + * * 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.29 2008/03/27 15:47:21 fw Exp $"; +/** + * @file + * I/O abstraction interface. + */ + +/* Extra debug messages in event add/delete/callback code: 0=off / 1=on */ +#define DEBUG_IO 0 #include -#include #include -#include #include #include +#include #include #include "array.h" #include "io.h" #include "log.h" -/* Enables extra debug messages in event add/delete/callback code. */ -/* #define DEBUG_IO */ - typedef struct { #ifdef PROTOTYPES void (*callback)(int, short); @@ -40,6 +42,7 @@ typedef struct { #define INIT_IOEVENT { NULL, -1, 0, NULL } #define IO_ERROR 4 +#define MAX_EVENTS 100 #ifdef HAVE_EPOLL_CREATE # define IO_USE_EPOLL 1 @@ -53,7 +56,7 @@ typedef struct { # ifdef HAVE_SYS_DEVPOLL_H # define IO_USE_DEVPOLL 1 # else -# ifdef HAVE_POLL +# if defined(HAVE_POLL) && defined(HAVE_POLL_H) # define IO_USE_POLL 1 # else # ifdef HAVE_SELECT @@ -84,6 +87,20 @@ static int io_masterfd; static int io_dispatch_kqueue(struct timeval *tv); static bool io_event_change_kqueue(int, short, const int action); + +#ifndef EV_SET +/* Taken from /usr/include/sys/event.h of FreeBSD 8.1 and required by all + * platforms that have kqueue but lack EV_SET() -- for example FreeBSD 4. */ +#define EV_SET(kevp, a, b, c, d, e, f) do { \ + struct kevent *__kevp__ = (kevp); \ + __kevp__->ident = (a); \ + __kevp__->filter = (b); \ + __kevp__->flags = (c); \ + __kevp__->fflags = (d); \ + __kevp__->data = (e); \ + __kevp__->udata = (f); \ +} while(0) +#endif #endif #ifdef IO_USE_POLL @@ -103,15 +120,20 @@ static bool io_event_change_devpoll(int fd, short what); #endif #ifdef IO_USE_SELECT +#include #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 "proc.h" /* for PROC_STAT (needed by conf.h) */ +#include "conn.h" /* for CONN_ID (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); +/* + * this is the first argument for select(), i.e. + * the largest fd registered, plus one. + */ +static int select_maxfd; +static int io_dispatch_select PARAMS((struct timeval *tv)); #ifndef IO_USE_EPOLL #define io_masterfd -1 @@ -122,13 +144,16 @@ static array io_events; static void io_docallback PARAMS((int fd, short what)); -#ifdef DEBUG_IO -static void io_debug(const char *s, int fd, int what) +#if DEBUG_IO +static void +io_debug(const char *s, int fd, int what) { Log(LOG_DEBUG, "%s: %d, %d\n", s, fd, what); } #else -static inline void io_debug(const char UNUSED *s,int UNUSED a, int UNUSED b) {/*NOTHING*/} +static inline void +io_debug(const char UNUSED *s,int UNUSED a, int UNUSED b) +{ /* NOTHING */ } #endif static io_event * @@ -152,39 +177,34 @@ io_dispatch_devpoll(struct timeval *tv) { struct dvpoll dvp; time_t sec = tv->tv_sec * 1000; - int i, total, ret, timeout = tv->tv_usec + sec; + int i, ret, timeout = tv->tv_usec + sec; short what; - struct pollfd p[100]; + struct pollfd p[MAX_EVENTS]; if (timeout < 0) timeout = 1000; - total = 0; - do { - dvp.dp_timeout = timeout; - dvp.dp_nfds = 100; - dvp.dp_fds = p; - ret = ioctl(io_masterfd, DP_POLL, &dvp); - total += ret; - if (ret <= 0) - return total; - for (i=0; i < ret ; i++) { - what = 0; - if (p[i].revents & (POLLIN|POLLPRI)) - what = IO_WANTREAD; - - if (p[i].revents & POLLOUT) - what |= IO_WANTWRITE; - - if (p[i].revents && !what) { - /* other flag is set, probably POLLERR */ - what = IO_ERROR; - } - io_docallback(p[i].fd, what); + dvp.dp_timeout = timeout; + dvp.dp_nfds = MAX_EVENTS; + dvp.dp_fds = p; + ret = ioctl(io_masterfd, DP_POLL, &dvp); + + for (i=0; i < ret ; i++) { + what = 0; + if (p[i].revents & (POLLIN|POLLPRI)) + what = IO_WANTREAD; + + if (p[i].revents & POLLOUT) + what |= IO_WANTWRITE; + + if (p[i].revents && !what) { + /* other flag is set, probably POLLERR */ + what = IO_ERROR; } - } while (ret == 100); + io_docallback(p[i].fd, what); + } - return total; + return ret; } @@ -223,8 +243,12 @@ io_library_init_devpoll(unsigned int eventsize) eventsize, io_masterfd); } #else -static inline void io_close_devpoll(int UNUSED x) {/* NOTHING */} -static inline void io_library_init_devpoll(unsigned int UNUSED ev) {/*NOTHING*/} +static inline void +io_close_devpoll(int UNUSED x) +{ /* NOTHING */ } +static inline void +io_library_init_devpoll(unsigned int UNUSED ev) +{ /* NOTHING */ } #endif @@ -327,8 +351,12 @@ io_library_init_poll(unsigned int eventsize) } } #else -static inline void io_close_poll(int UNUSED x) {/* NOTHING */} -static inline void io_library_init_poll(unsigned int UNUSED ev) {/*NOTHING*/} +static inline void +io_close_poll(int UNUSED x) +{ /* NOTHING */ } +static inline void +io_library_init_poll(unsigned int UNUSED ev) +{ /* NOTHING */ } #endif @@ -336,11 +364,15 @@ static inline void io_library_init_poll(unsigned int UNUSED ev) {/*NOTHING*/} static int io_dispatch_select(struct timeval *tv) { - fd_set readers_tmp = readers; - fd_set writers_tmp = writers; + fd_set readers_tmp; + fd_set writers_tmp; short what; int ret, i; int fds_ready; + + readers_tmp = readers; + writers_tmp = writers; + ret = select(select_maxfd + 1, &readers_tmp, &writers_tmp, NULL, tv); if (ret <= 0) return ret; @@ -384,6 +416,9 @@ io_library_init_select(unsigned int eventsize) 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 /* FD_SETSIZE */ library_initialized = true; } @@ -411,8 +446,12 @@ io_close_select(int fd) } } #else -static inline void io_library_init_select(int UNUSED x) {/* NOTHING */} -static inline void io_close_select(int UNUSED x) {/* NOTHING */} +static inline void +io_library_init_select(int UNUSED x) +{ /* NOTHING */ } +static inline void +io_close_select(int UNUSED x) +{ /* NOTHING */ } #endif /* SELECT */ @@ -435,37 +474,30 @@ 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]; + int i, ret, timeout = tv->tv_usec + sec; + struct epoll_event epoll_ev[MAX_EVENTS]; short type; if (timeout < 0) timeout = 1000; - do { - ret = epoll_wait(io_masterfd, epoll_ev, 100, timeout); - total += ret; - if (ret <= 0) - return total; + ret = epoll_wait(io_masterfd, epoll_ev, MAX_EVENTS, timeout); - for (i = 0; i < ret; i++) { - type = 0; - if (epoll_ev[i].events & (EPOLLERR | EPOLLHUP)) - type = IO_ERROR; + 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 & (EPOLLIN | EPOLLPRI)) + type |= IO_WANTREAD; - if (epoll_ev[i].events & EPOLLOUT) - type |= IO_WANTWRITE; + if (epoll_ev[i].events & EPOLLOUT) + type |= IO_WANTWRITE; - io_docallback(epoll_ev[i].data.fd, type); - } - - timeout = 0; - } while (ret == 100); + io_docallback(epoll_ev[i].data.fd, type); + } - return total; + return ret; } static void @@ -487,7 +519,9 @@ io_library_init_epoll(unsigned int eventsize) #endif } #else -static inline void io_library_init_epoll(unsigned int UNUSED ev) {/* NOTHING */} +static inline void +io_library_init_epoll(unsigned int UNUSED ev) +{ /* NOTHING */ } #endif /* IO_USE_EPOLL */ @@ -547,58 +581,50 @@ io_event_change_kqueue(int fd, short what, const int action) static int io_dispatch_kqueue(struct timeval *tv) { - int i, total = 0, ret; - struct kevent kev[100]; + int i, ret; + struct kevent kev[MAX_EVENTS]; struct kevent *newevents; struct timespec ts; int newevents_len; ts.tv_sec = tv->tv_sec; ts.tv_nsec = tv->tv_usec * 1000; - do { - newevents_len = (int) array_length(&io_evcache, sizeof (struct kevent)); - newevents = (newevents_len > 0) ? array_start(&io_evcache) : NULL; - assert(newevents_len >= 0); - - ret = kevent(io_masterfd, newevents, newevents_len, kev, 100, &ts); - if (newevents && ret != -1) - array_trunc(&io_evcache); - - total += ret; - if (ret <= 0) - return total; - - for (i = 0; i < ret; i++) { - io_debug("dispatch_kqueue: fd, kev.flags", (int)kev[i].ident, kev[i].flags); - if (kev[i].flags & (EV_EOF|EV_ERROR)) { - if (kev[i].flags & EV_ERROR) - Log(LOG_ERR, "kevent fd %d: EV_ERROR (%s)", - (int)kev[i].ident, strerror((int)kev[i].data)); - io_docallback((int)kev[i].ident, IO_ERROR); - continue; - } - - switch (kev[i].filter) { - case EVFILT_READ: - io_docallback((int)kev[i].ident, IO_WANTREAD); - break; - case EVFILT_WRITE: - io_docallback((int)kev[i].ident, IO_WANTWRITE); - break; - default: - LogDebug("Unknown kev.filter number %d for fd %d", - kev[i].filter, kev[i].ident); - /* Fall through */ - case EV_ERROR: - io_docallback((int)kev[i].ident, IO_ERROR); - break; - } + newevents_len = (int) array_length(&io_evcache, sizeof (struct kevent)); + newevents = (newevents_len > 0) ? array_start(&io_evcache) : NULL; + assert(newevents_len >= 0); + + ret = kevent(io_masterfd, newevents, newevents_len, kev, MAX_EVENTS, &ts); + if (newevents && ret != -1) + array_trunc(&io_evcache); + + for (i = 0; i < ret; i++) { + io_debug("dispatch_kqueue: fd, kev.flags", (int)kev[i].ident, kev[i].flags); + if (kev[i].flags & (EV_EOF|EV_ERROR)) { + if (kev[i].flags & EV_ERROR) + Log(LOG_ERR, "kevent fd %d: EV_ERROR (%s)", + (int)kev[i].ident, strerror((int)kev[i].data)); + io_docallback((int)kev[i].ident, IO_ERROR); + continue; } - ts.tv_sec = 0; - ts.tv_nsec = 0; - } while (ret == 100); - return total; + switch (kev[i].filter) { + case EVFILT_READ: + io_docallback((int)kev[i].ident, IO_WANTREAD); + break; + case EVFILT_WRITE: + io_docallback((int)kev[i].ident, IO_WANTWRITE); + break; + default: + LogDebug("Unknown kev.filter number %d for fd %d", + kev[i].filter, kev[i].ident); + /* Fall through */ + case EV_ERROR: + io_docallback((int)kev[i].ident, IO_ERROR); + break; + } + } + + return ret; } static void @@ -607,13 +633,15 @@ io_library_init_kqueue(unsigned int eventsize) io_masterfd = kqueue(); Log(LOG_INFO, - "IO subsystem: kqueue (initial maxfd %u, masterfd %d)", + "IO subsystem: kqueue (initial maxfd %u, masterfd %d).", eventsize, io_masterfd); if (io_masterfd >= 0) library_initialized = true; } #else -static inline void io_library_init_kqueue(unsigned int UNUSED ev) {/* NOTHING */} +static inline void +io_library_init_kqueue(unsigned int UNUSED ev) +{ /* NOTHING */ } #endif @@ -622,15 +650,7 @@ io_library_init(unsigned int eventsize) { if (library_initialized) return true; -#ifdef IO_USE_SELECT -#ifndef FD_SETSIZE - Log(LOG_WARNING, - "FD_SETSIZE undefined, don't know how many descriptors select() can handle on your platform ..."); -#else - if (eventsize >= FD_SETSIZE) - eventsize = FD_SETSIZE - 1; -#endif /* FD_SETSIZE */ -#endif /* IO_USE_SELECT */ + if ((eventsize > 0) && !array_alloc(&io_events, sizeof(io_event), (size_t)eventsize)) eventsize = 0; @@ -707,7 +727,7 @@ io_event_create(int fd, short what, void (*cbfunc) (int, short)) assert(fd >= 0); #if defined(IO_USE_SELECT) && defined(FD_SETSIZE) - if (fd >= FD_SETSIZE) { + if (io_masterfd < 0 && fd >= FD_SETSIZE) { Log(LOG_ERR, "fd %d exceeds FD_SETSIZE (%u) (select can't handle more file descriptors)", fd, FD_SETSIZE); @@ -786,6 +806,18 @@ io_setnonblock(int fd) return fcntl(fd, F_SETFL, flags) == 0; } +bool +io_setcloexec(int fd) +{ + int flags = fcntl(fd, F_GETFD); + if (flags == -1) + return false; +#ifdef FD_CLOEXEC + flags |= FD_CLOEXEC; +#endif + + return fcntl(fd, F_SETFD, flags) == 0; +} bool io_close(int fd)