From 560c19871dfb899e254f5f3e68d4d2d567f4940c Mon Sep 17 00:00:00 2001 From: Michael Poole Date: Fri, 22 Sep 2006 02:38:21 +0000 Subject: [PATCH] Allow multiple ioset backends. configure.in: Add framework to check for ioset backends. src/Makefile.am (noinst_DATA): Reorganize for easier patching. (checkversion): Avoid spurious error message if $(GNU_ARCH) is missing. (EXTRA_srvx_SOURCES): Reorganize for easier patching; add ioset-select.c. (srvx_SOURCES): Add ioset-impl.h. src/ioset.c (ioset.h): #include "ioset-impl.h" instead. (clock_skew): Make non-static for backends. (engine): New variable. (active_fd): New variable (kind of kludgy). (io_engine_*): New variables, only one exists so far. (ioset_init): New function. (ioset_cleanup): Call engine's cleanup function. (ioset_add): Call engine's add function. (ioset_listen): New function. (ioset_connect): Call engine's update functions at appropriate points. (ioset_try_write): Call engine's update function on success. (ioset_close): Change signature; check against active_fd; call engine's remove function. (ioset_accept): New function. (ioset_buffered_read): Update fd->state instead of fd->eof and call engine's update function as appropriate. Use active_fd. (ioset_line_read): Check fd->state instead of fd->eof. (debug_fdsets): Move to ioset-select.c. (ioset_events): New function. (ioset_run): Move most of the logic into ioset-select loop function; call it. Rename "select_timeout" to "timeout" to match. (ioset_write): Call engine->update function. (ioset_printf): New function. src/ioset.h (common.h): #include this header to get PRINTF_LIKE macro. (struct io_fd): Replace 'connected' and 'eof' fields with 'state' field. (ioset_init): Declare new function. (ioset_listen): Declare new function. (ioset_printf): Declare new function. (ioset_close): Update signature. src/main.c (main): Call ioset_init(). src/mod-sockcheck.c (sockcheck_free_client): Can unconditionally call ioset_close() now. (expand_var): Always use C99 type names. (sockcheck_begin_test): Can unconditionally call ioset_close() now. (sockcheck_read_conf): Only warn about unknown host if the user set one; it is silly to arn about unknown host `(null)'. src/proto-common.c (socket_destroyed): Check fd->state rather than fd->eof. (close_socket): Update signature for ioset_close(). git-archimport-id: srvx@srvx.net--2006/srvx--devo--1.3--patch-38 --- ChangeLog | 67 +++++++++ configure.in | 12 ++ src/Makefile.am | 28 +++- src/ioset-impl.h | 43 ++++++ src/ioset-select.c | 155 ++++++++++++++++++++ src/ioset.c | 340 +++++++++++++++++++++++++++----------------- src/ioset.h | 14 +- src/main.c | 3 +- src/mod-sockcheck.c | 21 ++- src/proto-common.c | 7 +- 10 files changed, 536 insertions(+), 154 deletions(-) create mode 100644 src/ioset-impl.h create mode 100644 src/ioset-select.c diff --git a/ChangeLog b/ChangeLog index bbcef8f..1ae7256 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,73 @@ # arch-tag: automatic-ChangeLog--srvx@srvx.net--2006/srvx--devo--1.3 # +2006-09-22 02:38:21 GMT Michael Poole patch-38 + + Summary: + Allow multiple ioset backends. + Revision: + srvx--devo--1.3--patch-38 + + configure.in: Add framework to check for ioset backends. + + src/Makefile.am (noinst_DATA): Reorganize for easier patching. + (checkversion): Avoid spurious error message if $(GNU_ARCH) is missing. + (EXTRA_srvx_SOURCES): Reorganize for easier patching; add ioset-select.c. + (srvx_SOURCES): Add ioset-impl.h. + + src/ioset.c (ioset.h): #include "ioset-impl.h" instead. + (clock_skew): Make non-static for backends. + (engine): New variable. + (active_fd): New variable (kind of kludgy). + (io_engine_*): New variables, only one exists so far. + (ioset_init): New function. + (ioset_cleanup): Call engine's cleanup function. + (ioset_add): Call engine's add function. + (ioset_listen): New function. + (ioset_connect): Call engine's update functions at appropriate points. + (ioset_try_write): Call engine's update function on success. + (ioset_close): Change signature; check against active_fd; call engine's + remove function. + (ioset_accept): New function. + (ioset_buffered_read): Update fd->state instead of fd->eof and call + engine's update function as appropriate. Use active_fd. + (ioset_line_read): Check fd->state instead of fd->eof. + (debug_fdsets): Move to ioset-select.c. + (ioset_events): New function. + (ioset_run): Move most of the logic into ioset-select loop function; + call it. Rename "select_timeout" to "timeout" to match. + (ioset_write): Call engine->update function. + (ioset_printf): New function. + + src/ioset.h (common.h): #include this header to get PRINTF_LIKE macro. + (struct io_fd): Replace 'connected' and 'eof' fields with 'state' field. + (ioset_init): Declare new function. + (ioset_listen): Declare new function. + (ioset_printf): Declare new function. + (ioset_close): Update signature. + + src/main.c (main): Call ioset_init(). + + src/mod-sockcheck.c (sockcheck_free_client): Can unconditionally call + ioset_close() now. + (expand_var): Always use C99 type names. + (sockcheck_begin_test): Can unconditionally call ioset_close() now. + (sockcheck_read_conf): Only warn about unknown host if the user set + one; it is silly to arn about unknown host `(null)'. + + src/proto-common.c (socket_destroyed): Check fd->state rather than + fd->eof. + (close_socket): Update signature for ioset_close(). + + new files: + src/.arch-ids/ioset-impl.h.id src/.arch-ids/ioset-select.c.id + src/ioset-impl.h src/ioset-select.c + + modified files: + ChangeLog configure.in src/Makefile.am src/ioset.c src/ioset.h + src/main.c src/mod-sockcheck.c src/proto-common.c + + 2006-09-22 02:15:55 GMT Michael Poole patch-37 Summary: diff --git a/configure.in b/configure.in index 38bd08c..dd6474b 100644 --- a/configure.in +++ b/configure.in @@ -241,6 +241,18 @@ else AC_MSG_ERROR([Unknown IRC dialect $withval]) fi +AC_MSG_CHECKING([I/O multiplexing backends]) +IOMUXES="" + +if test "x$ac_cv_func_select" = xyes ; then + AC_DEFINE(WITH_IOSET_SELECT, 1, [Define if using the select() I/O backend]) + MODULE_OBJS="$MODULE_OBJS ioset-select.\$(OBJEXT)" + IOMUXES="$IOMUXES select" +fi + +IOMUXES=`echo $IOMUXES | sed 's/^ +//'` +AC_MSG_RESULT($IOMUXES) + AC_ARG_WITH(getopt, [ --without-getopt Disables building of the GNU getopt library], [if test "$withval" = no; then diff --git a/src/Makefile.am b/src/Makefile.am index 8cdf7bb..37df3cd 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -3,7 +3,17 @@ LIBS = @LIBS@ @RX_LIBS@ noinst_PROGRAMS = srvx EXTRA_PROGRAMS = checkdb globtest -noinst_DATA = chanserv.help global.help modcmd.help nickserv.help opserv.help saxdb.help sendmail.help mod-sockcheck.help mod-helpserv.help mod-memoserv.help +noinst_DATA = \ + chanserv.help \ + global.help \ + modcmd.help \ + nickserv.help \ + opserv.help \ + saxdb.help \ + sendmail.help \ + mod-helpserv.help \ + mod-memoserv.help \ + mod-sockcheck.help EXTRA_DIST = $(noinst_DATA) BUILT_SOURCES = arch-version.h noinst_HEADERS = arch-version.h @@ -12,7 +22,7 @@ if HAS_GNU_ARCH .PHONY: checkversion arch-version.h: checkversion checkversion: - @$(GNU_ARCH) logs -f >/dev/null || exit 0; \ + @$(GNU_ARCH) logs -f >/dev/null 2>&1 || exit 0; \ TMPFILE=`mktemp arch-version.h.XXXXXX` || exit 1 ; \ echo "#define ARCH_VERSION \"`$(GNU_ARCH) logs -f | tail -n 1`\"" >> $$TMPFILE ; \ if diff -q arch-version.h $$TMPFILE >/dev/null 2>&1 ; then \ @@ -24,7 +34,17 @@ checkversion: fi endif -EXTRA_srvx_SOURCES = alloc-slab.c alloc-srvx.c proto-bahamut.c proto-common.c proto-p10.c mod-snoop.c mod-memoserv.c mod-helpserv.c mod-sockcheck.c +EXTRA_srvx_SOURCES = \ + alloc-slab.c \ + alloc-srvx.c \ + ioset-select.c \ + proto-bahamut.c \ + proto-common.c \ + proto-p10.c \ + mod-snoop.c \ + mod-memoserv.c \ + mod-helpserv.c \ + mod-sockcheck.c srvx_LDADD = @MODULE_OBJS@ srvx_DEPENDENCIES = @MODULE_OBJS@ srvx_SOURCES = \ @@ -40,7 +60,7 @@ srvx_SOURCES = \ hash.c hash.h \ heap.c heap.h \ helpfile.c helpfile.h \ - ioset.c ioset.h \ + ioset.c ioset.h ioset-impl.h \ log.c log.h \ main.c common.h \ md5.c md5.h \ diff --git a/src/ioset-impl.h b/src/ioset-impl.h new file mode 100644 index 0000000..d6b3045 --- /dev/null +++ b/src/ioset-impl.h @@ -0,0 +1,43 @@ +/* srvx event loop implementation details + * Copyright 2006 srvx Development Team + * + * This file is part of srvx. + * + * srvx 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. + * + * 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 srvx; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#if !defined(IOSET_IMPL_H) +#define IOSET_IMPL_H + +#include "ioset.h" + +struct timeval; + +#define fd_wants_reads(FD) ((FD)->wants_reads || (FD)->state == IO_LISTENING) +#define fd_wants_writes(FD) (((FD)->send.get != (FD)->send.put) || (FD)->state == IO_CONNECTING) + +struct io_engine { + const char *name; + int (*init)(void); + void (*add)(struct io_fd *fd); + void (*remove)(struct io_fd *fd); + void (*update)(struct io_fd *fd); + int (*loop)(struct timeval *timeout); + void (*cleanup)(void); +}; + +void ioset_events(struct io_fd *fd, int readable, int writable); + +#endif /* !defined(IOSET_IMPL_H) */ diff --git a/src/ioset-select.c b/src/ioset-select.c new file mode 100644 index 0000000..d25738d --- /dev/null +++ b/src/ioset-select.c @@ -0,0 +1,155 @@ +/* ioset select() backend for srvx + * Copyright 2002-2006 srvx Development Team + * + * This file is part of srvx. + * + * srvx 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. + * + * 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 srvx; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "ioset-impl.h" +#include "common.h" +#include "log.h" + +#include +#include + +#ifdef HAVE_SYS_SELECT_H +# include +#endif + +#ifdef HAVE_SYS_SOCKET_H +# include +#endif + +extern int clock_skew; +static struct io_fd **fds; +static unsigned int fds_size; +static fd_set read_fds, write_fds; + +static int +ioset_select_init(void) +{ + return 1; +} + +static void +ioset_select_add(struct io_fd *fd) +{ + if ((unsigned)fd->fd >= fds_size) { + unsigned int old_size = fds_size; + fds_size = fd->fd + 8; + fds = realloc(fds, fds_size*sizeof(*fds)); + memset(fds+old_size, 0, (fds_size-old_size)*sizeof(*fds)); + } + fds[fd->fd] = fd; +} + +static void +ioset_select_remove(struct io_fd *fd) +{ + FD_CLR(fd->fd, &read_fds); + FD_CLR(fd->fd, &write_fds); +} + +static void +ioset_select_update(struct io_fd *fd) +{ + (void)fd; +} + +static void +ioset_select_cleanup(void) +{ + free(fds); +} + +#if 0 +#define debug_fdsets(MSG, NFDS, READ_FDS, WRITE_FDS, EXCEPT_FDS, SELECT_TIMEOUT) (void)0 +#else +static void +debug_fdsets(const char *msg, int nfds, fd_set *read_fds, fd_set *write_fds, fd_set *except_fds, struct timeval *select_timeout) { + static const char *flag_text[8] = { "---", "r", "w", "rw", "e", "er", "ew", "erw" }; + char buf[MAXLEN]; + int pos, ii, flags; + struct timeval now; + + for (pos=ii=0; iitv_sec, select_timeout->tv_usec); + } else { + log_module(MAIN_LOG, LOG_DEBUG, "%s, at "FMT_TIME_T".%06ld:%s (no timeout)", msg, now.tv_sec, now.tv_usec, buf); + } +} +#endif + +static int +ioset_select_loop(struct timeval *timeout) +{ + struct io_fd *fd; + unsigned int nn; + int select_result; + int max_fd; + + /* Set up read_fds and write_fds fdsets. */ + FD_ZERO(&read_fds); + FD_ZERO(&write_fds); + max_fd = -1; + for (nn=0; nn #endif -#ifdef HAVE_SYS_SELECT_H -#include -#endif #ifdef HAVE_SYS_SOCKET_H #include #endif -#ifndef IOSET_DEBUG -#define IOSET_DEBUG 0 -#endif - #define IS_EOL(CH) ((CH) == '\n') extern int uplink_connect(void); -static int clock_skew; +int clock_skew; int do_write_dbs; int do_reopen; - -static struct io_fd **fds; -static unsigned int fds_size; -static fd_set read_fds, write_fds; +static struct io_engine *engine; +static struct io_fd *active_fd; static void ioq_init(struct ioq *ioq, int size) { @@ -94,9 +85,43 @@ ioq_grow(struct ioq *ioq) { return new_size - ioq->put; } +extern struct io_engine io_engine_kqueue; +extern struct io_engine io_engine_epoll; +extern struct io_engine io_engine_win32; +extern struct io_engine io_engine_select; + +void +ioset_init(void) +{ + assert(engine == NULL); + +#if WITH_IOSET_KQUEUE + if (!engine && io_engine_kqueue.init()) + engine = &io_engine_kqueue; +#endif + +#if WITH_IOSET_EPOLL + if (!engine && io_engine_epoll.init()) + engine = &io_engine_epoll; +#endif + +#if WITH_IOSET_WIN32 + if (!engine && io_engine_win32.init()) + engine = &io_engine_win32; +#endif + + if (engine) { + /* we found one that works */ + } else if (io_engine_select.init()) + engine = &io_engine_select; + else + log_module(MAIN_LOG, LOG_FATAL, "No usable I/O engine found."); + log_module(MAIN_LOG, LOG_DEBUG, "Using %s I/O engine.", engine->name); +} + void ioset_cleanup(void) { - free(fds); + engine->cleanup(); } struct io_fd * @@ -114,18 +139,59 @@ ioset_add(int fd) { res->fd = fd; ioq_init(&res->send, 1024); ioq_init(&res->recv, 1024); - if ((unsigned)fd >= fds_size) { - unsigned int old_size = fds_size; - fds_size = fd + 8; - fds = realloc(fds, fds_size*sizeof(*fds)); - memset(fds+old_size, 0, (fds_size-old_size)*sizeof(*fds)); - } - fds[fd] = res; + engine->add(res); flags = fcntl(fd, F_GETFL); fcntl(fd, F_SETFL, flags|O_NONBLOCK); return res; } +struct io_fd *ioset_listen(struct sockaddr *local, unsigned int sa_size, void *data, void (*accept_cb)(struct io_fd *listener, struct io_fd *new_connect)) +{ + struct io_fd *io_fd; + unsigned int opt; + int res; + int fd; + + fd = socket(local ? local->sa_family : PF_INET, SOCK_STREAM, 0); + if (fd < 0) { + log_module(MAIN_LOG, LOG_ERROR, "Unable to create listening socket: %s", strerror(errno)); + return NULL; + } + + if (local && sa_size) { + res = bind(fd, local, sa_size); + if (res < 0) { + log_module(MAIN_LOG, LOG_ERROR, "Unable to bind listening socket %d: %s", fd, strerror(errno)); + close(fd); + return NULL; + } + + opt = 1; + res = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt)); + if (res < 0) { + log_module(MAIN_LOG, LOG_WARNING, "Unable to mark listener address as re-usable: %s", strerror(errno)); + } + } + + res = listen(fd, 1); + if (res < 0) { + log_module(MAIN_LOG, LOG_ERROR, "Unable to listen on socket %d: %s", fd, strerror(errno)); + close(fd); + return NULL; + } + + io_fd = ioset_add(fd); + if (!io_fd) { + close(fd); + return NULL; + } + io_fd->state = IO_LISTENING; + io_fd->data = data; + io_fd->accept_cb = accept_cb; + engine->update(io_fd); + return io_fd; +} + struct io_fd * ioset_connect(struct sockaddr *local, unsigned int sa_size, const char *peer, unsigned int port, int blocking, void *data, void (*connect_cb)(struct io_fd *fd, int error)) { int fd, res; @@ -171,23 +237,28 @@ ioset_connect(struct sockaddr *local, unsigned int sa_size, const char *peer, un close(fd); return NULL; } + io_fd->state = IO_CONNECTING; io_fd->data = data; io_fd->connect_cb = connect_cb; if (res < 0) { switch (errno) { case EINPROGRESS: /* only if !blocking */ + engine->update(io_fd); return io_fd; default: log_module(MAIN_LOG, LOG_ERROR, "connect(%s:%d) (fd %d) returned errno %d (%s).", peer, port, io_fd->fd, errno, strerror(errno)); /* then fall through */ case EHOSTUNREACH: case ECONNREFUSED: - ioset_close(io_fd->fd, 1); + ioset_close(io_fd, 1); + engine->update(io_fd); return NULL; } } + io_fd->state = IO_CONNECTED; if (connect_cb) connect_cb(io_fd, ((res < 0) ? errno : 0)); + engine->update(io_fd); return io_fd; } @@ -207,20 +278,23 @@ ioset_try_write(struct io_fd *fd) { fd->send.get += res; if (fd->send.get == fd->send.size) fd->send.get = 0; + engine->update(fd); } } void -ioset_close(int fd, int os_close) { - struct io_fd *fdp; - if (!(fdp = fds[fd])) - return; - fds[fd] = NULL; +ioset_close(struct io_fd *fdp, int os_close) { + if (!fdp) + return; + if (active_fd == fdp) + active_fd = NULL; if (fdp->destroy_cb) fdp->destroy_cb(fdp); if (fdp->send.get != fdp->send.put) { - int flags = fcntl(fd, F_GETFL); - fcntl(fd, F_SETFL, flags&~O_NONBLOCK); + int flags; + + flags = fcntl(fdp->fd, F_GETFL); + fcntl(fdp->fd, F_SETFL, flags&~O_NONBLOCK); ioset_try_write(fdp); /* it may need to send the beginning of the buffer now.. */ if (fdp->send.get != fdp->send.put) @@ -229,10 +303,37 @@ ioset_close(int fd, int os_close) { free(fdp->send.buf); free(fdp->recv.buf); if (os_close) - close(fd); + close(fdp->fd); + engine->remove(fdp); free(fdp); - FD_CLR(fd, &read_fds); - FD_CLR(fd, &write_fds); +} + +static void +ioset_accept(struct io_fd *listener) +{ + struct io_fd *old_active_fd; + struct io_fd *new_fd; + int fd; + + fd = accept(listener->fd, NULL, 0); + if (fd < 0) { + log_module(MAIN_LOG, LOG_ERROR, "Unable to accept new connection on listener %d: %s", listener->fd, strerror(errno)); + return; + } + + new_fd = ioset_add(fd); + new_fd->state = IO_CONNECTED; + old_active_fd = active_fd; + active_fd = new_fd; + listener->accept_cb(listener, new_fd); + assert(active_fd == NULL || active_fd == new_fd); + if (active_fd == new_fd) { + if (new_fd->send.get != new_fd->send.put) + ioset_try_write(new_fd); + else + engine->update(new_fd); + } + active_fd = old_active_fd; } static int @@ -253,7 +354,7 @@ ioset_find_line_length(struct io_fd *fd) { static void ioset_buffered_read(struct io_fd *fd) { int put_avail, nbr, fdnum; - + if (!(put_avail = ioq_put_avail(&fd->recv))) put_avail = ioq_grow(&fd->recv); nbr = read(fd->fd, fd->recv.buf + fd->recv.put, put_avail); @@ -264,14 +365,16 @@ ioset_buffered_read(struct io_fd *fd) { default: log_module(MAIN_LOG, LOG_ERROR, "Unexpected read() error %d on fd %d: %s", errno, fd->fd, strerror(errno)); /* Just flag it as EOF and call readable_cb() to notify the fd's owner. */ - fd->eof = 1; - fd->wants_reads = 0; + fd->state = IO_CLOSED; fd->readable_cb(fd); + if (active_fd == fd) + engine->update(fd); } } else if (nbr == 0) { - fd->eof = 1; - fd->wants_reads = 0; + fd->state = IO_CLOSED; fd->readable_cb(fd); + if (active_fd == fd) + engine->update(fd); } else { if (fd->line_len == 0) { unsigned int pos; @@ -290,10 +393,15 @@ ioset_buffered_read(struct io_fd *fd) { fd->recv.put = 0; fdnum = fd->fd; while (fd->wants_reads && (fd->line_len > 0)) { + struct io_fd *old_active; + + old_active = active_fd; + active_fd = fd; fd->readable_cb(fd); - if (!fds[fdnum]) - break; /* make sure they didn't close on us */ - ioset_find_line_length(fd); + if (active_fd) + ioset_find_line_length(fd); + if (old_active != fd) + active_fd = old_active; } } } @@ -301,7 +409,7 @@ ioset_buffered_read(struct io_fd *fd) { int ioset_line_read(struct io_fd *fd, char *dest, int max) { int avail, done; - if (fd->eof && (!ioq_get_avail(&fd->recv) || (fd->line_len < 0))) + if ((fd->state == IO_CLOSED) && (!ioq_get_avail(&fd->recv) || (fd->line_len < 0))) return 0; if (fd->line_len < 0) return -1; @@ -326,41 +434,55 @@ ioset_line_read(struct io_fd *fd, char *dest, int max) { return max; } -#if 1 -#define debug_fdsets(MSG, NFDS, READ_FDS, WRITE_FDS, EXCEPT_FDS, SELECT_TIMEOUT) (void)0 -#else -static void -debug_fdsets(const char *msg, int nfds, fd_set *read_fds, fd_set *write_fds, fd_set *except_fds, struct timeval *select_timeout) { - static const char *flag_text[8] = { "---", "r", "w", "rw", "e", "er", "ew", "erw" }; - char buf[MAXLEN]; - int pos, ii, flags; - struct timeval now; - - for (pos=ii=0; iitv_sec, select_timeout->tv_usec); - } else { - log_module(MAIN_LOG, LOG_DEBUG, "%s, at "FMT_TIME_T".%06ld:%s (no timeout)", msg, now.tv_sec, now.tv_usec, buf); +void +ioset_events(struct io_fd *fd, int readable, int writable) +{ + if (!fd || (!readable && !writable)) + return; + active_fd = fd; + switch (fd->state) { + case IO_CLOSED: + break; + case IO_LISTENING: + if (active_fd && readable) + ioset_accept(fd); + break; + case IO_CONNECTING: + assert(active_fd == NULL || active_fd == fd); + if (active_fd && writable) { + socklen_t arglen; + int rc; + arglen = sizeof(rc); + if (getsockopt(fd->fd, SOL_SOCKET, SO_ERROR, &rc, &arglen) < 0) + rc = errno; + fd->state = IO_CONNECTED; + if (fd->connect_cb) + fd->connect_cb(fd, rc); + if (active_fd == fd) + engine->update(fd); + } + /* and fall through */ + case IO_CONNECTED: + assert(active_fd == NULL || active_fd == fd); + if (active_fd && readable) { + if (fd->line_reads) + ioset_buffered_read(fd); + else + fd->readable_cb(fd); + } + + assert(active_fd == NULL || active_fd == fd); + if (active_fd && writable) + ioset_try_write(fd); + break; } } -#endif void ioset_run(void) { extern struct io_fd *socket_io_fd; - struct timeval select_timeout; - unsigned int nn; - int select_result, max_fd; + struct timeval timeout; time_t wakey; - struct io_fd *fd; while (!quit_services) { while (!socket_io_fd) @@ -369,66 +491,13 @@ ioset_run(void) { /* How long to sleep? (fill in select_timeout) */ wakey = timeq_next(); if ((wakey - now) < 0) - select_timeout.tv_sec = 0; + timeout.tv_sec = 0; else - select_timeout.tv_sec = wakey - now; - select_timeout.tv_usec = 0; - - /* Set up read_fds and write_fds fdsets. */ - FD_ZERO(&read_fds); - FD_ZERO(&write_fds); - max_fd = 0; - for (nn=0; nnwants_reads) - FD_SET(nn, &read_fds); - if ((fd->send.get != fd->send.put) || !fd->connected) - FD_SET(nn, &write_fds); - } + timeout.tv_sec = wakey - now; + timeout.tv_usec = 0; - /* Check for activity, update time. */ - debug_fdsets("Entering select", max_fd+1, &read_fds, &write_fds, NULL, &select_timeout); - select_result = select(max_fd + 1, &read_fds, &write_fds, NULL, &select_timeout); - debug_fdsets("After select", max_fd+1, &read_fds, &write_fds, NULL, &select_timeout); - now = time(NULL) + clock_skew; - if (select_result < 0) { - if (errno != EINTR) { - log_module(MAIN_LOG, LOG_ERROR, "select() error %d: %s", errno, strerror(errno)); - close_socket(); - } - continue; - } - - /* Call back anybody that has connect or read activity and wants to know. */ - for (nn=0; nnline_reads) - ioset_buffered_read(fd); - else - fd->readable_cb(fd); - } - if (FD_ISSET(nn, &write_fds) && !fd->connected) { - socklen_t arglen; - int rc; - - arglen = sizeof(rc); - if (getsockopt(fd->fd, SOL_SOCKET, SO_ERROR, &rc, &arglen) < 0) - rc = errno; - fd->connected = 1; - if (fd->connect_cb) - fd->connect_cb(fd, rc); - } - /* Note: check whether write FD is still set, since the - * connect_cb() might close the FD, making us dereference - * a free()'d pointer for the fd. - */ - if (FD_ISSET(nn, &write_fds) && (fd->send.get != fd->send.put)) - ioset_try_write(fd); - } + if (engine->loop(&timeout)) + continue; /* Call any timeq events we need to call. */ timeq_run(); @@ -460,6 +529,21 @@ ioset_write(struct io_fd *fd, const char *buf, unsigned int nbw) { fd->send.put += nbw; if (fd->send.put == fd->send.size) fd->send.put = 0; + engine->update(fd); +} + +int +ioset_printf(struct io_fd *fd, const char *fmt, ...) { + char tmpbuf[MAXLEN]; + va_list ap; + int res; + + va_start(ap, fmt); + res = vsnprintf(tmpbuf, sizeof(tmpbuf), fmt, ap); + va_end(ap); + if (res > 0 && (size_t)res <= sizeof(tmpbuf)) + ioset_write(fd, tmpbuf, res); + return res; } void diff --git a/src/ioset.h b/src/ioset.h index 0c4f84f..be27332 100644 --- a/src/ioset.h +++ b/src/ioset.h @@ -21,7 +21,9 @@ #if !defined(IOSET_H) #define IOSET_H -/* Forward declare, since ioset_connect() takes a sockaddr argument. */ +#include "common.h" + +/* Forward declare, since functions take these as arguments. */ struct sockaddr; struct ioq { @@ -32,27 +34,29 @@ struct ioq { struct io_fd { int fd; void *data; - unsigned int connected : 1; + enum { IO_CLOSED, IO_LISTENING, IO_CONNECTING, IO_CONNECTED } state; unsigned int wants_reads : 1; unsigned int line_reads : 1; - unsigned int eof : 1; int line_len; struct ioq send; struct ioq recv; + void (*accept_cb)(struct io_fd *listener, struct io_fd *new_connect); void (*connect_cb)(struct io_fd *fd, int error); void (*readable_cb)(struct io_fd *fd); void (*destroy_cb)(struct io_fd *fd); }; - extern int do_write_dbs; extern int do_reopen; +void ioset_init(void); struct io_fd *ioset_add(int fd); +struct io_fd *ioset_listen(struct sockaddr *local, unsigned int sa_size, void *data, void (*accept_cb)(struct io_fd *listener, struct io_fd *new_connect)); struct io_fd *ioset_connect(struct sockaddr *local, unsigned int sa_size, const char *hostname, unsigned int port, int blocking, void *data, void (*connect_cb)(struct io_fd *fd, int error)); void ioset_run(void); void ioset_write(struct io_fd *fd, const char *buf, unsigned int nbw); +int ioset_printf(struct io_fd *fd, const char *fmt, ...) PRINTF_LIKE(2, 3); int ioset_line_read(struct io_fd *fd, char *buf, int maxlen); -void ioset_close(int fd, int os_close); +void ioset_close(struct io_fd *fd, int os_close); void ioset_cleanup(void); void ioset_set_time(unsigned long new_now); diff --git a/src/main.c b/src/main.c index 2517516..fe73b4f 100644 --- a/src/main.c +++ b/src/main.c @@ -1,5 +1,5 @@ /* main.c - srvx - * Copyright 2000-2004 srvx Development Team + * Copyright 2000-2006 srvx Development Team * * This file is part of srvx. * @@ -825,6 +825,7 @@ int main(int argc, char *argv[]) if (debug) log_debug(); timeq_init(); + ioset_init(); init_structs(); init_parse(); modcmd_init(); diff --git a/src/mod-sockcheck.c b/src/mod-sockcheck.c index 08ce625..8091c8a 100644 --- a/src/mod-sockcheck.c +++ b/src/mod-sockcheck.c @@ -1,5 +1,5 @@ /* mod-sockcheck.c - insecure proxy checking - * Copyright 2000-2004 srvx Development Team + * Copyright 2000-2006 srvx Development Team * * This file is part of srvx. * @@ -230,8 +230,8 @@ sockcheck_free_client(struct sockcheck_client *client) log_module(PC_LOG, LOG_INFO, "Goodbye %s (%p)! I set you free!", client->addr->hostname, client); } verify(client); - if (client->fd) - ioset_close(client->fd->fd, 1); + ioset_close(client->fd, 1); + client->fd = NULL; sockcheck_list_unref(client->tests); free(client->read); free(client->resp_state); @@ -287,13 +287,9 @@ expand_var(const struct sockcheck_client *client, char var, char **p_expansion, extern struct cManagerNode cManager; const char *expansion; unsigned int exp_length; -#ifndef __SOLARIS__ - u_int32_t exp4; - u_int16_t exp2; -#else uint32_t exp4; uint16_t exp2; -#endif + /* expand variable */ switch (var) { case 'c': @@ -661,10 +657,8 @@ sockcheck_begin_test(struct sockcheck_client *client) struct io_fd *io_fd; verify(client); - if (client->fd) { - ioset_close(client->fd->fd, 1); - client->fd = NULL; - } + ioset_close(client->fd, 1); + client->fd = NULL; do { client->state = client->tests->list[client->test_index]; client->read_pos = 0; @@ -1161,7 +1155,8 @@ sockcheck_read_conf(void) } else { sockcheck_conf.local_addr_len = 0; sockcheck_conf.local_addr = NULL; - log_module(PC_LOG, LOG_ERROR, "Error: Unable to get host named `%s', not checking a specific address.", str); + if (str) + log_module(PC_LOG, LOG_ERROR, "Error: Unable to get host named `%s', not checking a specific address.", str); } } } diff --git a/src/proto-common.c b/src/proto-common.c index a8b2888..68cdfbc 100644 --- a/src/proto-common.c +++ b/src/proto-common.c @@ -1,5 +1,5 @@ /* proto-common.c - common IRC protocol parsing/sending support - * Copyright 2000-2004 srvx Development Team + * Copyright 2000-2006 srvx Development Team * * This file is part of srvx. * @@ -92,7 +92,7 @@ uplink_readable(struct io_fd *fd) { void socket_destroyed(struct io_fd *fd) { - if (fd && fd->eof) + if (fd && fd->state != IO_CONNECTED) log_module(MAIN_LOG, LOG_ERROR, "Connection to server lost."); socket_io_fd = NULL; cManager.uplink->state = DISCONNECTED; @@ -275,7 +275,8 @@ close_socket(void) replay_connected = 0; socket_destroyed(socket_io_fd); } else { - ioset_close(socket_io_fd->fd, 1); + ioset_close(socket_io_fd, 1); + socket_io_fd = NULL; } } -- 2.20.1