/*
+ * 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.30 2008/04/03 14:17:42 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 <assert.h>
-#include <stdlib.h>
#include <string.h>
-#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
+#include <sys/stat.h>
#include <fcntl.h>
#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);
#define INIT_IOEVENT { NULL, -1, 0, NULL }
#define IO_ERROR 4
+#define MAX_EVENTS 100
#ifdef HAVE_EPOLL_CREATE
# define IO_USE_EPOLL 1
# 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
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
#endif
#ifdef IO_USE_SELECT
+#include <sys/time.h>
#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
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 *
{
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;
}
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
}
}
#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
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;
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;
}
}
}
#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 */
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
#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 */
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
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
{
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;
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)