From 79b5ee62665a460214046003cdbe1b4b4f1fa39e Mon Sep 17 00:00:00 2001 From: pk910 Date: Sun, 27 May 2012 00:36:36 +0200 Subject: [PATCH] modified IOMultiplexer (added epoll & kevent support) --- Makefile.am | 2 + configure.ac | 2 +- src/IOEngine.h | 18 +++ src/IOEngine_epoll.c | 152 ++++++++++++++++++++++ src/IOEngine_kevent.c | 166 ++++++++++++++++++++++++ src/IOEngine_select.c | 60 ++++----- src/IOHandler.c | 292 +++++++++++++++++++++++++++++++++++------- src/IOHandler.h | 5 +- src/UserClient.c | 2 +- src/UserSession.c | 8 +- src/overall.h | 4 +- 11 files changed, 621 insertions(+), 90 deletions(-) create mode 100644 src/IOEngine_epoll.c create mode 100644 src/IOEngine_kevent.c diff --git a/Makefile.am b/Makefile.am index 225077c..1abc4d4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -12,6 +12,8 @@ transirc_SOURCES = src/version.c \ src/ConfigParser.c \ src/IOHandler.c \ src/IOEngine_select.c \ + src/IOEngine_epoll.c \ + src/IOEngine_kevent.c \ src/main.c \ src/ServerSocket.c \ src/tools.c \ diff --git a/configure.ac b/configure.ac index 61224de..9aea42a 100644 --- a/configure.ac +++ b/configure.ac @@ -57,7 +57,7 @@ AC_ARG_ENABLE([debug], CFLAGS="$CFLAGS -D_GNU_SOURCE" # Checks for header files. -AC_CHECK_HEADERS([arpa/inet.h netdb.h netinet/in.h stdlib.h string.h sys/socket.h unistd.h windows.h winsock2.h]) +AC_CHECK_HEADERS([arpa/inet.h netdb.h netinet/in.h stdlib.h string.h sys/socket.h unistd.h windows.h winsock2.h sys/epoll.h sys/event.h]) # Checks for typedefs, structures, and compiler characteristics. diff --git a/src/IOEngine.h b/src/IOEngine.h index dfcbe98..0f33e81 100644 --- a/src/IOEngine.h +++ b/src/IOEngine.h @@ -19,8 +19,22 @@ #include "overall.h" struct IODescriptor; +enum IOType; +enum IOStatus; +enum IOEventType; + +#define timeval_is_bigger(x,y) ((x->tv_sec > y->tv_sec) || (x->tv_sec == y->tv_sec && x->tv_usec > y->tv_usec)) +#define timeval_is_smaler(x,y) ((x->tv_sec < y->tv_sec) || (x->tv_sec == y->tv_sec && x->tv_usec < y->tv_usec)) + +enum IOLogType { + IOLOG_DEBUG, + IOLOG_WARNING, + IOLOG_ERROR, + IOLOG_FATAL +}; extern struct IODescriptor *first_descriptor; +extern struct IODescriptor *timer_priority; struct IOEngine { const char *name; @@ -34,6 +48,10 @@ struct IOEngine { #define iohandler_wants_writes(IOFD) (IOFD->writebuf.bufpos || IOFD->state == IO_CONNECTING) +void iohandler_log(enum IOLogType type, char *text, ...); void iohandler_events(struct IODescriptor *iofd, int readable, int writeable); +char *iohandler_iotype_name(enum IOType type); +char *iohandler_iostatus_name(enum IOStatus status); +char *iohandler_ioeventtype_name(enum IOEventType type); #endif diff --git a/src/IOEngine_epoll.c b/src/IOEngine_epoll.c new file mode 100644 index 0000000..b87953d --- /dev/null +++ b/src/IOEngine_epoll.c @@ -0,0 +1,152 @@ +/* IOEngine_epoll.c - TransparentIRC 0.1 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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 this program. If not, see . + */ +#include "IOEngine.h" +#include "IOHandler.h" + +#ifdef HAVE_SYS_EPOLL_H +#include + +#define MAX_EVENTS 32 + +static int epoll_fd; + +static int engine_epoll_init() { + epoll_fd = epoll_create(1024); + if (epoll_fd < 0) + return 0; + return 1; +} + +static void engine_epoll_add(struct IODescriptor *iofd) { + if(iofd->type == IOTYPE_TIMER) return; + //add descriptor to the epoll queue + struct epoll_event evt; + int res; + + evt.events = EPOLLHUP | EPOLLIN | (iohandler_wants_writes(iofd) ? EPOLLOUT : 0); + evt.data.ptr = iofd; + res = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, iofd->fd, &evt); + if(res < 0) { + iohandler_log(IOLOG_ERROR, "could not add IODescriptor %d to epoll queue. (returned: %d)", iofd->fd, res); + } +} + +static void engine_epoll_remove(struct IODescriptor *iofd) { + if(iofd->type == IOTYPE_TIMER) return; + struct epoll_event evt; + epoll_ctl(epoll_fd, EPOLL_CTL_DEL, iofd->fd, &evt); +} + +static void engine_epoll_update(struct IODescriptor *iofd) { + if(iofd->type == IOTYPE_TIMER) return; + struct epoll_event evt; + int res; + + evt.events = EPOLLHUP | EPOLLIN | (iohandler_wants_writes(iofd) ? EPOLLOUT : 0); + evt.data.ptr = iofd; + res = epoll_ctl(epoll_fd, EPOLL_CTL_MOD, iofd->fd, &evt); + if(res < 0) { + iohandler_log(IOLOG_ERROR, "could not update IODescriptor %d in epoll queue. (returned: %d)", iofd->fd, res); + } +} + +static void engine_epoll_loop(struct timeval *timeout) { + struct epoll_event evts[MAX_EVENTS]; + struct timeval now, tdiff; + int msec; + int events; + int epoll_result; + + gettimeofday(&now, NULL); + + while(timer_priority) { + tdiff.tv_sec = timer_priority->timeout.tv_sec - now.tv_sec; + tdiff.tv_usec = timer_priority->timeout.tv_usec - now.tv_usec; + if(tdiff.tv_sec < 0 || (tdiff.tv_sec == 0 && tdiff.tv_usec <= 0)) { + iohandler_events(timer_priority, 0, 0); + iohandler_close(timer_priority); //also sets timer_priority to the next timed element + continue; + } else if(tdiff.tv_usec < 0) { + tdiff.tv_sec--; + tdiff.tv_usec += 1000000; //1 sec + } + if(timeval_is_smaler((&tdiff), timeout)) { + timeout->tv_sec = tdiff.tv_sec; + timeout->tv_usec = tdiff.tv_usec; + } + break; + } + + msec = timeout ? ((timeout->tv_sec * 1000 + timeout->tv_usec / 1000) + (timeout->tv_usec % 1000 != 0 ? 1 : 0)) : -1; + + //select system call + epoll_result = epoll_wait(epoll_fd, evts, MAX_EVENTS, msec); + + if (epoll_result < 0) { + if (errno != EINTR) { + iohandler_log(IOLOG_FATAL, "epoll_wait() failed with errno %d: %s", errno, strerror(errno)); + return; + } + } else { + int i; + for(i = 0; i < epoll_result; i++) { + events = evts[i].events; + iohandler_events(evts[i].data.ptr, (events & (EPOLLIN | EPOLLHUP)), (events & EPOLLOUT)); + } + } + + //check timers + while(timer_priority) { + tdiff.tv_sec = timer_priority->timeout.tv_sec - now.tv_sec; + tdiff.tv_usec = timer_priority->timeout.tv_usec - now.tv_usec; + if(tdiff.tv_sec < 0 || (tdiff.tv_sec == 0 && tdiff.tv_usec <= 0)) { + iohandler_events(timer_priority, 0, 0); + iohandler_close(timer_priority); //also sets timer_priority to the next timed element + continue; + } + break; + } + +} + +static void engine_epoll_cleanup() { + close(epoll_fd); +} + +struct IOEngine engine_epoll = { + .name = "epoll", + .init = engine_epoll_init, + .add = engine_epoll_add, + .remove = engine_epoll_remove, + .update = engine_epoll_update, + .loop = engine_epoll_loop, + .cleanup = engine_epoll_cleanup, +}; + +#else + +struct IOEngine engine_epoll = { + .name = "epoll", + .init = NULL, + .add = NULL, + .remove = NULL, + .update = NULL, + .loop = NULL, + .cleanup = NULL, +}; + +#endif diff --git a/src/IOEngine_kevent.c b/src/IOEngine_kevent.c new file mode 100644 index 0000000..5d6f55b --- /dev/null +++ b/src/IOEngine_kevent.c @@ -0,0 +1,166 @@ +/* IOengine_kevent.c - TransparentIRC 0.1 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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 this program. If not, see . + */ +#include "IOEngine.h" +#include "IOHandler.h" + +#ifdef HAVE_SYS_EVENT_H +#include + +#define MAX_EVENTS 32 + +static int kevent_fd; + +static int engine_kevent_init() { + kevent_fd = kqueue(); + if (kevent_fd < 0) + return 0; + return 1; +} + +static void engine_kevent_add(struct IODescriptor *iofd) { + if(iofd->type == IOTYPE_TIMER) return; + //add descriptor to the kevent queue + struct kevent changes[2]; + int nchanges = 0; + int res; + + EV_SET(&changes[nchanges++], iofd->fd, EVFILT_READ, EV_ADD, 0, 0, iofd); + if (iohandler_wants_writes(iofd)) + EV_SET(&changes[nchanges++], iofd->fd, EVFILT_WRITE, EV_ADD, 0, 0, iofd); + + res = kevent(kevent_fd, changes, nchanges, NULL, 0, NULL); + if(res < 0) + iohandler_log(IOLOG_ERROR, "could not add IODescriptor %d to kevent queue. (returned: %d)", res); + } +} + +static void engine_kevent_remove(struct IODescriptor *iofd) { + if(iofd->type == IOTYPE_TIMER) return; + struct kevent changes[2]; + int nchanges = 0; + + EV_SET(&changes[nchanges++], iofd->fd, EVFILT_READ, EV_DELETE, 0, 0, iofd); + EV_SET(&changes[nchanges++], iofd->fd, EVFILT_WRITE, EV_DELETE, 0, 0, iofd); + kevent(kevent_fd, changes, nchanges, NULL, 0, NULL); +} + +static void engine_kevent_update(struct IODescriptor *iofd) { + if(iofd->type == IOTYPE_TIMER) return; + struct kevent changes[2]; + int nchanges = 0; + int res; + + EV_SET(&changes[nchanges++], iofd->fd, EVFILT_READ, EV_ADD, 0, 0, iofd); + EV_SET(&changes[nchanges++], iofd->fd, EVFILT_WRITE, iohandler_wants_writes(iofd) ? EV_ADD : EV_DELETE, 0, 0, iofd); + + res = kevent(kevent_fd, changes, nchanges, NULL, 0, NULL); + if(res < 0) { + iohandler_log(IOLOG_ERROR, "could not update IODescriptor %d in kevent queue. (returned: %d)", res); + } +} + +static void engine_kevent_loop(struct timeval *timeout) { + struct kevent evts[MAX_EVENTS]; + struct timeval now, tdiff; + struct timespec ts, *ptr + int msec; + int events; + int kevent_result; + + gettimeofday(&now, NULL); + + while(timer_priority) { + tdiff.tv_sec = timer_priority->timeout.tv_sec - now.tv_sec; + tdiff.tv_usec = timer_priority->timeout.tv_usec - now.tv_usec; + if(tdiff.tv_sec < 0 || (tdiff.tv_sec == 0 && tdiff.tv_usec <= 0)) { + iohandler_events(timer_priority, 0, 0); + iohandler_close(timer_priority); //also sets timer_priority to the next timed element + continue; + } else if(tdiff.tv_usec < 0) { + tdiff.tv_sec--; + tdiff.tv_usec += 1000000; //1 sec + } + if(timeval_is_smaler((&tdiff), timeout)) { + timeout->tv_sec = tdiff.tv_sec; + timeout->tv_usec = tdiff.tv_usec; + } + break; + } + + if (timeout) { + ts.tv_sec = timeout->tv_sec; + ts.tv_nsec = timeout->tv_usec * 1000; + pts = &ts; + } else { + pts = NULL; + } + + //select system call + kevent_result = kevent(kq_fd, NULL, 0, events, MAX_EVENTS, pts); + + if (kevent_result < 0) { + if (errno != EINTR) { + iohandler_log(IOLOG_FATAL, "kevent() failed with errno %d: %s", errno, strerror(errno)); + return; + } + } else { + int i; + for(i = 0; i < kevent_result; i++) + iohandler_events(evts[i].udata, (evts[i].filter == EVFILT_READ), (evts[i].filter == EVFILT_WRITE)); + } + + //check timers + while(timer_priority) { + tdiff.tv_sec = timer_priority->timeout.tv_sec - now.tv_sec; + tdiff.tv_usec = timer_priority->timeout.tv_usec - now.tv_usec; + if(tdiff.tv_sec < 0 || (tdiff.tv_sec == 0 && tdiff.tv_usec <= 0)) { + iohandler_events(timer_priority, 0, 0); + iohandler_close(timer_priority); //also sets timer_priority to the next timed element + continue; + } + break; + } + +} + +static void engine_kevent_cleanup() { + close(kevent_fd); +} + +struct IOEngine engine_kevent = { + .name = "kevent", + .init = engine_kevent_init, + .add = engine_kevent_add, + .remove = engine_kevent_remove, + .update = engine_kevent_update, + .loop = engine_kevent_loop, + .cleanup = engine_kevent_cleanup, +}; + +#else + +struct IOEngine engine_kevent = { + .name = "kevent", + .init = NULL, + .add = NULL, + .remove = NULL, + .update = NULL, + .loop = NULL, + .cleanup = NULL, +}; + +#endif diff --git a/src/IOEngine_select.c b/src/IOEngine_select.c index e37b104..8303308 100644 --- a/src/IOEngine_select.c +++ b/src/IOEngine_select.c @@ -57,25 +57,24 @@ static void engine_select_loop(struct timeval *timeout) { if(iohandler_wants_writes(iofd)) FD_SET(iofd->fd, &write_fds); } - if(iofd->type == IOTYPE_TIMER || iofd->timeout.tv_sec || iofd->timeout.tv_usec) { - tdiff.tv_sec = iofd->timeout.tv_sec - now.tv_sec; - tdiff.tv_usec = iofd->timeout.tv_usec - now.tv_usec; - - if(tdiff.tv_sec < 0 || (tdiff.tv_sec == 0 && tdiff.tv_usec <= 0)) { - //exec timer - iohandler_events(iofd, 0, 0); - if(iofd->type == IOTYPE_TIMER) - iohandler_close(iofd); - continue; - } else if(tdiff.tv_usec < 0) { - tdiff.tv_sec--; - tdiff.tv_usec += 1000000; //1 sec - } - if(timeval_is_smaler((&tdiff), timeout)) { - timeout->tv_sec = tdiff.tv_sec; - timeout->tv_usec = tdiff.tv_usec; - } + } + + while(timer_priority) { + tdiff.tv_sec = timer_priority->timeout.tv_sec - now.tv_sec; + tdiff.tv_usec = timer_priority->timeout.tv_usec - now.tv_usec; + if(tdiff.tv_sec < 0 || (tdiff.tv_sec == 0 && tdiff.tv_usec <= 0)) { + iohandler_events(timer_priority, 0, 0); + iohandler_close(timer_priority); //also sets timer_priority to the next timed element + continue; + } else if(tdiff.tv_usec < 0) { + tdiff.tv_sec--; + tdiff.tv_usec += 1000000; //1 sec + } + if(timeval_is_smaler((&tdiff), timeout)) { + timeout->tv_sec = tdiff.tv_sec; + timeout->tv_usec = tdiff.tv_usec; } + break; } //select system call @@ -83,7 +82,7 @@ static void engine_select_loop(struct timeval *timeout) { if (select_result < 0) { if (errno != EINTR) { - //hard fail + iohandler_log(IOLOG_FATAL, "select() failed with errno %d: %s", errno, strerror(errno)); return; } } @@ -99,19 +98,20 @@ static void engine_select_loop(struct timeval *timeout) { continue; } } - if(iofd->type == IOTYPE_TIMER || iofd->timeout.tv_sec || iofd->timeout.tv_usec) { - tdiff.tv_sec = iofd->timeout.tv_sec - now.tv_sec; - tdiff.tv_usec = iofd->timeout.tv_usec - now.tv_usec; - - if(tdiff.tv_sec < 0 || (tdiff.tv_sec == 0 && tdiff.tv_usec <= 0)) { - //exec timer - iohandler_events(iofd, 0, 0); - if(iofd->type == IOTYPE_TIMER) - iohandler_close(iofd); - continue; - } + } + + //check timers + while(timer_priority) { + tdiff.tv_sec = timer_priority->timeout.tv_sec - now.tv_sec; + tdiff.tv_usec = timer_priority->timeout.tv_usec - now.tv_usec; + if(tdiff.tv_sec < 0 || (tdiff.tv_sec == 0 && tdiff.tv_usec <= 0)) { + iohandler_events(timer_priority, 0, 0); + iohandler_close(timer_priority); //also sets timer_priority to the next timed element + continue; } + break; } + } static void engine_select_cleanup() { diff --git a/src/IOHandler.c b/src/IOHandler.c index 1843acc..7a9f3b3 100644 --- a/src/IOHandler.c +++ b/src/IOHandler.c @@ -17,33 +17,135 @@ #include "IOHandler.h" #include "IOEngine.h" +#define MAXLOG 1024 + struct IODescriptor *first_descriptor = NULL; +struct IODescriptor *timer_priority = NULL; + +void iohandler_log(enum IOLogType type, char *text, ...) { + va_list arg_list; + char logBuf[MAXLOG+1]; + int pos; + logBuf[0] = '\0'; + va_start(arg_list, text); + pos = vsnprintf(logBuf, MAXLOG - 1, text, arg_list); + va_end(arg_list); + if (pos < 0 || pos > (MAXLOG - 1)) pos = MAXLOG - 1; + logBuf[pos] = '\n'; + logBuf[pos+1] = '\0'; + + //do something with logBuf + //... +} /* IO Engines */ extern struct IOEngine engine_select; /* select system call (should always be useable) */ +extern struct IOEngine engine_kevent; +extern struct IOEngine engine_epoll; struct IOEngine *engine = NULL; static void iohandler_init_engine() { //try other engines + if(!engine && engine_kevent.init && engine_kevent.init()) + engine = &engine_kevent; + if(!engine && engine_epoll.init && engine_epoll.init()) + engine = &engine_epoll; - if(engine) { - //found an useable IO engine - } else if (engine_select.init()) - engine = &engine_select; - else { - //STDERR: found no useable IO engine + if (!engine) { + if(engine_select.init()) + engine = &engine_select; + else { + iohandler_log(IOLOG_FATAL, "found no useable IO engine"); + return; + } + } + iohandler_log(IOLOG_DEBUG, "using %s IO engine", engine->name); +} + +static void iohandler_append(struct IODescriptor *descriptor) { + struct timeval *timeout = ((descriptor->timeout.tv_sec || descriptor->timeout.tv_usec) ? &descriptor->timeout : NULL); + if(timeout) { + struct IODescriptor *iofd; + int set_priority = 1; + descriptor->timeout = *timeout; + if(timer_priority) + iofd = timer_priority; + else + iofd = first_descriptor; + if(iofd) { + for(;;iofd = iofd->next) { + if(timeval_is_smaler(timeout, (&iofd->timeout))) { + descriptor->prev = iofd->prev; + descriptor->next = iofd; + iofd->prev = descriptor; + if(iofd->prev) + iofd->prev->next = descriptor; + if(set_priority) + timer_priority = descriptor; + break; + } + if(iofd == timer_priority) + set_priority = 0; + if(iofd->next == NULL) { + descriptor->next = NULL; + descriptor->prev = iofd; + iofd->next = descriptor; + if(set_priority) + timer_priority = descriptor; + break; + } + } + } else { + descriptor->prev = NULL; + descriptor->next = NULL; + first_descriptor = descriptor; + timer_priority = descriptor; + } + + } else { + descriptor->prev = NULL; + descriptor->next = first_descriptor; + if(first_descriptor) + first_descriptor->prev = descriptor; + first_descriptor = descriptor; } } -struct IODescriptor *iohandler_add(int sockfd, enum IOType type, iohandler_callback *callback) { +static void iohandler_remove(struct IODescriptor *descriptor, int engine_remove) { + //remove IODescriptor from the list + if(descriptor->prev) + descriptor->prev->next = descriptor->next; + else + first_descriptor = descriptor->next; + if(descriptor->next) + descriptor->next->prev = descriptor->prev; + if(descriptor == timer_priority) + timer_priority = descriptor->next; + + if(engine_remove) + engine->remove(descriptor); + if(descriptor->readbuf.buffer) + free(descriptor->readbuf.buffer); + if(descriptor->writebuf.buffer) + free(descriptor->writebuf.buffer); + iohandler_log(IOLOG_DEBUG, "removed IODescriptor (%d) of type `%s`", descriptor->fd, iohandler_iotype_name(descriptor->type)); + free(descriptor); +} + +struct IODescriptor *iohandler_add(int sockfd, enum IOType type, struct timeval *timeout, iohandler_callback *callback) { //just add a new IODescriptor struct IODescriptor *descriptor = calloc(1, sizeof(*descriptor)); - if(!descriptor) return NULL; + if(!descriptor) { + iohandler_log(IOLOG_ERROR, "could not allocate memory for IODescriptor in %s:%d", __FILE__, __LINE__); + return NULL; + } descriptor->fd = sockfd; descriptor->type = type; descriptor->state = IO_CLOSED; descriptor->callback = callback; + if(timeout) + descriptor->timeout = *timeout; if(type != IOTYPE_TIMER) { descriptor->readbuf.buffer = malloc(IO_READ_BUFLEN + 2); descriptor->readbuf.bufpos = 0; @@ -62,30 +164,28 @@ struct IODescriptor *iohandler_add(int sockfd, enum IOType type, iohandler_callb engine->add(descriptor); //add IODescriptor to the list - descriptor->prev = NULL; - descriptor->next = first_descriptor; - if(first_descriptor) - first_descriptor->prev = descriptor; - first_descriptor = descriptor; + iohandler_append(descriptor); + iohandler_log(IOLOG_DEBUG, "added custom socket descriptor (%d) as type `%s`", sockfd, iohandler_iotype_name(type)); return descriptor; } -static void iohandler_remove(struct IODescriptor *descriptor, int engine_remove) { - //remove IODescriptor from the list +void iohandler_set_timeout(struct IODescriptor *descriptor, struct timeval *timeout) { if(descriptor->prev) descriptor->prev->next = descriptor->next; else first_descriptor = descriptor->next; if(descriptor->next) descriptor->next->prev = descriptor->prev; - if(engine_remove) - engine->remove(descriptor); - if(descriptor->readbuf.buffer) - free(descriptor->readbuf.buffer); - if(descriptor->writebuf.buffer) - free(descriptor->writebuf.buffer); - free(descriptor); + if(descriptor == timer_priority) + timer_priority = descriptor->next; + if(timeout) + descriptor->timeout = *timeout; + else { + descriptor->timeout.tv_sec = 0; + descriptor->timeout.tv_usec = 0; + } + iohandler_append(descriptor); } static void iohandler_increase_iobuf(struct IOBuffer *iobuf, size_t required) { @@ -99,10 +199,12 @@ static void iohandler_increase_iobuf(struct IOBuffer *iobuf, size_t required) { struct IODescriptor *iohandler_timer(struct timeval timeout, iohandler_callback *callback) { struct IODescriptor *descriptor; - descriptor = iohandler_add(-1, IOTYPE_TIMER, callback); - if(!descriptor) return NULL; - descriptor->timeout = timeout; - engine->update(descriptor); + descriptor = iohandler_add(-1, IOTYPE_TIMER, &timeout, callback); + if(!descriptor) { + iohandler_log(IOLOG_ERROR, "could not allocate memory for IODescriptor in %s:%d", __FILE__, __LINE__); + return NULL; + } + iohandler_log(IOLOG_DEBUG, "added timer descriptor (sec: %d; usec: %d)", timeout.tv_sec, timeout.tv_usec); return descriptor; } @@ -121,6 +223,7 @@ struct IODescriptor *iohandler_connect(const char *hostname, unsigned int port, hints.ai_socktype = SOCK_STREAM; hints.ai_flags |= AI_CANONNAME; if (getaddrinfo (hostname, NULL, &hints, &res)) { + iohandler_log(IOLOG_ERROR, "could not resolve %d to an IP address", hostname); return NULL; } while (res) { @@ -139,7 +242,10 @@ struct IODescriptor *iohandler_connect(const char *hostname, unsigned int port, if(ip6) { sockfd = socket(AF_INET6, SOCK_STREAM, 0); - if(sockfd == -1) return NULL; + if(sockfd == -1) { + iohandler_log(IOLOG_ERROR, "could not create socket in %s:%d", __FILE__, __LINE__); + return NULL; + } ip6->sin6_family = AF_INET6; ip6->sin6_port = htons(port); @@ -166,7 +272,10 @@ struct IODescriptor *iohandler_connect(const char *hostname, unsigned int port, dstaddrlen = sizeof(*ip6); } else if(ip4) { sockfd = socket(AF_INET, SOCK_STREAM, 0); - if(sockfd == -1) return NULL; + if(sockfd == -1) { + iohandler_log(IOLOG_ERROR, "could not create socket in %s:%d", __FILE__, __LINE__); + return NULL; + } ip4->sin_family = AF_INET; ip4->sin_port = htons(port); @@ -195,16 +304,19 @@ struct IODescriptor *iohandler_connect(const char *hostname, unsigned int port, return NULL; //make sockfd unblocking #if defined(F_GETFL) - flags = fcntl(sockfd, F_GETFL); - fcntl(sockfd, F_SETFL, flags|O_NONBLOCK); - flags = fcntl(sockfd, F_GETFD); - fcntl(sockfd, F_SETFD, flags|FD_CLOEXEC); + { + int flags; + flags = fcntl(sockfd, F_GETFL); + fcntl(sockfd, F_SETFL, flags|O_NONBLOCK); + flags = fcntl(sockfd, F_GETFD); + fcntl(sockfd, F_SETFD, flags|FD_CLOEXEC); + } #else /* I hope you're using the Win32 backend or something else that * automatically marks the file descriptor non-blocking... */ #endif - descriptor = iohandler_add(sockfd, IOTYPE_CLIENT, callback); + descriptor = iohandler_add(sockfd, IOTYPE_CLIENT, NULL, callback); if(!descriptor) { close(sockfd); return NULL; @@ -213,6 +325,7 @@ struct IODescriptor *iohandler_connect(const char *hostname, unsigned int port, descriptor->state = IO_CONNECTING; descriptor->read_lines = 1; engine->update(descriptor); + iohandler_log(IOLOG_DEBUG, "added client socket (%d) connecting to %s:%d", sockfd, hostname, port); return descriptor; } @@ -271,16 +384,19 @@ struct IODescriptor *iohandler_listen(const char *hostname, unsigned int port, i return NULL; //make sockfd unblocking #if defined(F_GETFL) - flags = fcntl(sockfd, F_GETFL); - fcntl(sockfd, F_SETFL, flags|O_NONBLOCK); - flags = fcntl(sockfd, F_GETFD); - fcntl(sockfd, F_SETFD, flags|FD_CLOEXEC); + { + int flags; + flags = fcntl(sockfd, F_GETFL); + fcntl(sockfd, F_SETFL, flags|O_NONBLOCK); + flags = fcntl(sockfd, F_GETFD); + fcntl(sockfd, F_SETFD, flags|FD_CLOEXEC); + } #else /* I hope you're using the Win32 backend or something else that * automatically marks the file descriptor non-blocking... */ #endif - descriptor = iohandler_add(sockfd, IOTYPE_SERVER, callback); + descriptor = iohandler_add(sockfd, IOTYPE_SERVER, NULL, callback); if(!descriptor) { close(sockfd); return NULL; @@ -288,6 +404,7 @@ struct IODescriptor *iohandler_listen(const char *hostname, unsigned int port, i listen(sockfd, 1); descriptor->state = IO_LISTENING; engine->update(descriptor); + iohandler_log(IOLOG_DEBUG, "added server socket (%d) listening on %s:%d", sockfd, hostname, port); return descriptor; } @@ -297,11 +414,18 @@ void iohandler_write(struct IODescriptor *iofd, const char *line) { } void iohandler_send(struct IODescriptor *iofd, const char *data, size_t datalen) { - if(iofd->type == IOTYPE_TIMER || iofd->state == IO_CLOSED) return; //can not write to timer? :D + if(iofd->type == IOTYPE_TIMER || iofd->state == IO_CLOSED) { + iohandler_log(IOLOG_ERROR, "could not write to socket (%s)", (iofd->type == IOTYPE_TIMER ? "IOTYPE_TIMER" : "IO_CLOSED")); + return; + } + iohandler_log(IOLOG_DEBUG, "add %d to writebuf (fd: %d): %s", datalen, iofd->fd, data); if(iofd->writebuf.buflen < iofd->writebuf.bufpos + datalen) { + iohandler_log(IOLOG_DEBUG, "increase writebuf (curr: %d) to %d (+%d bytes)", iofd->writebuf.buflen, iofd->writebuf.bufpos + datalen, (iofd->writebuf.bufpos + datalen - iofd->writebuf.buflen)); iohandler_increase_iobuf(&iofd->writebuf, iofd->writebuf.bufpos + datalen); - if(iofd->writebuf.buflen < iofd->writebuf.bufpos + datalen) + if(iofd->writebuf.buflen < iofd->writebuf.bufpos + datalen) { + iohandler_log(IOLOG_ERROR, "increase writebuf (curr: %d) to %d (+%d bytes) FAILED", iofd->writebuf.buflen, iofd->writebuf.bufpos + datalen, (iofd->writebuf.bufpos + datalen - iofd->writebuf.buflen)); return; + } } memcpy(iofd->writebuf.buffer + iofd->writebuf.bufpos, data, datalen); iofd->writebuf.bufpos += datalen; @@ -324,26 +448,32 @@ void iohandler_printf(struct IODescriptor *iofd, const char *text, ...) { void iohandler_try_write(struct IODescriptor *iofd) { if(!iofd->writebuf.bufpos) return; + iohandler_log(IOLOG_DEBUG, "write writebuf (%d bytes) to socket (fd: %d)", iofd->writebuf.bufpos, iofd->fd); int res = send(iofd->fd, iofd->writebuf.buffer, iofd->writebuf.bufpos, 0); if(res < 0) { if (errno != EAGAIN) { - //error: could not write + iohandler_log(IOLOG_ERROR, "could not write to socket (fd: %d): %d - %s", iofd->fd, errno, strerror(errno)); } } else { iofd->writebuf.bufpos -= res; - engine->update(iofd); + if(iofd->state != IO_CLOSED) + engine->update(iofd); } } void iohandler_close(struct IODescriptor *iofd) { int engine_remove = 1; + iofd->state = IO_CLOSED; if(iofd->writebuf.bufpos) { //try to send everything before closing #if defined(F_GETFL) - flags = fcntl(sockfd, F_GETFL); - fcntl(sockfd, F_SETFL, flags & ~O_NONBLOCK); - flags = fcntl(sockfd, F_GETFD); - fcntl(sockfd, F_SETFD, flags|FD_CLOEXEC); + { + int flags; + flags = fcntl(iofd->fd, F_GETFL); + fcntl(iofd->fd, F_SETFL, flags & ~O_NONBLOCK); + flags = fcntl(iofd->fd, F_GETFD); + fcntl(iofd->fd, F_SETFD, flags|FD_CLOEXEC); + } #else engine_remove = 0; engine->remove(iofd); @@ -357,11 +487,13 @@ void iohandler_close(struct IODescriptor *iofd) { } void iohandler_update(struct IODescriptor *iofd) { + iohandler_log(IOLOG_DEBUG, "external call to iohandler_update (fd: %d)", iofd->fd); engine->update(iofd); } static void iohandler_trigger_event(struct IOEvent *event) { if(!event->iofd->callback) return; + iohandler_log(IOLOG_DEBUG, "triggering event (%s) for %s (fd: %d)", iohandler_ioeventtype_name(event->type), iohandler_iotype_name(event->iofd->type), event->iofd->fd); event->iofd->callback(event); } @@ -378,7 +510,7 @@ void iohandler_events(struct IODescriptor *iofd, int readable, int writeable) { if(readable) { callback_event.data.accept_fd = accept(iofd->fd, NULL, 0); if(callback_event.data.accept_fd < 0) { - //error: could not accept + iohandler_log(IOLOG_ERROR, "could not accept client (server fd: %d): %d - %s", iofd->fd, errno, strerror(errno)); } else callback_event.type = IOEVENT_ACCEPT; } @@ -391,6 +523,7 @@ void iohandler_events(struct IODescriptor *iofd, int readable, int writeable) { if (getsockopt(iofd->fd, SOL_SOCKET, SO_ERROR, &callback_event.data.errid, &arglen) < 0) callback_event.data.errid = errno; iofd->state = IO_CLOSED; + engine->update(iofd); } else if(writeable) { callback_event.type = IOEVENT_CONNECTED; iofd->state = IO_CONNECTED; @@ -404,11 +537,13 @@ void iohandler_events(struct IODescriptor *iofd, int readable, int writeable) { if(bytes <= 0) { if (errno != EAGAIN) { iofd->state = IO_CLOSED; + engine->update(iofd); callback_event.type = IOEVENT_CLOSED; callback_event.data.errid = errno; } } else { int i, used_bytes = 0; + iohandler_log(IOLOG_DEBUG, "received %d bytes (fd: %d). readbuf position: %d", bytes, iofd->fd, iofd->readbuf.bufpos); iofd->readbuf.bufpos += bytes; callback_event.type = IOEVENT_RECV; for(i = 0; i < iofd->readbuf.bufpos; i++) { @@ -417,11 +552,13 @@ void iohandler_events(struct IODescriptor *iofd, int readable, int writeable) { else if(iofd->readbuf.buffer[i] == '\n' || iofd->readbuf.buffer[i] == '\r') { iofd->readbuf.buffer[i] = 0; callback_event.data.recv_str = iofd->readbuf.buffer + used_bytes; + iohandler_log(IOLOG_DEBUG, "parsed line (%d bytes): %s", i - used_bytes, iofd->readbuf.buffer + used_bytes); used_bytes = i+1; iohandler_trigger_event(&callback_event); } else if(i + 1 - used_bytes >= LINELEN) { //512 max iofd->readbuf.buffer[i] = 0; callback_event.data.recv_str = iofd->readbuf.buffer + used_bytes; + iohandler_log(IOLOG_DEBUG, "parsed and stripped line (%d bytes): %s", i - used_bytes, iofd->readbuf.buffer + used_bytes); for(; i < iofd->readbuf.bufpos; i++) { //skip the rest of the line if(iofd->readbuf.buffer[i] == '\n' || (iofd->readbuf.buffer[i] == '\r' && iofd->readbuf.buffer[i+1] != '\n')) { break; @@ -432,9 +569,11 @@ void iohandler_events(struct IODescriptor *iofd, int readable, int writeable) { } } if(used_bytes) { - if(used_bytes == iofd->readbuf.bufpos) + if(used_bytes == iofd->readbuf.bufpos) { iofd->readbuf.bufpos = 0; - else { + iohandler_log(IOLOG_DEBUG, "readbuf fully processed (set buffer position to 0)"); + } else { + iohandler_log(IOLOG_DEBUG, "readbuf rest: %d bytes (used %d bytes)", iofd->readbuf.bufpos - used_bytes, used_bytes); memmove(iofd->readbuf.buffer, iofd->readbuf.buffer + used_bytes, iofd->readbuf.bufpos - used_bytes); iofd->readbuf.bufpos -= used_bytes; } @@ -464,3 +603,58 @@ void iohandler_poll() { } } +//debugging functions +char *iohandler_iotype_name(enum IOType type) { + switch(type) { + case IOTYPE_UNKNOWN: + return "IOTYPE_UNKNOWN"; + case IOTYPE_SERVER: + return "IOTYPE_SERVER"; + case IOTYPE_CLIENT: + return "IOTYPE_CLIENT"; + case IOTYPE_STDIN: + return "IOTYPE_STDIN"; + case IOTYPE_TIMER: + return "IOTYPE_TIMER"; + default: + return "(UNDEFINED)"; + } +} + +char *iohandler_iostatus_name(enum IOStatus status) { + switch(status) { + case IO_CLOSED: + return "IO_CLOSED"; + case IO_LISTENING: + return "IO_LISTENING"; + case IO_CONNECTING: + return "IO_CONNECTING"; + case IO_CONNECTED: + return "IO_CONNECTED"; + default: + return "(UNDEFINED)"; + } +} + +char *iohandler_ioeventtype_name(enum IOEventType type) { + switch(type) { + case IOEVENT_IGNORE: + return "IOEVENT_IGNORE"; + case IOEVENT_READABLE: + return "IOEVENT_READABLE"; + case IOEVENT_RECV: + return "IOEVENT_RECV"; + case IOEVENT_CONNECTED: + return "IOEVENT_CONNECTED"; + case IOEVENT_NOTCONNECTED: + return "IOEVENT_NOTCONNECTED"; + case IOEVENT_CLOSED: + return "IOEVENT_CLOSED"; + case IOEVENT_ACCEPT: + return "IOEVENT_ACCEPT"; + case IOEVENT_TIMEOUT: + return "IOEVENT_TIMEOUT"; + default: + return "(UNDEFINED)"; + } +} diff --git a/src/IOHandler.h b/src/IOHandler.h index 3e9e705..76fb2cf 100644 --- a/src/IOHandler.h +++ b/src/IOHandler.h @@ -57,7 +57,6 @@ struct IOBuffer { struct IODescriptor { int fd; - FILE *file; enum IOType type; enum IOStatus state; struct timeval timeout; @@ -81,8 +80,7 @@ struct IOEvent { } data; }; -struct IODescriptor *iohandler_add(int sockfd, enum IOType type, iohandler_callback *callback); -struct IODescriptor *iohandler_file(FILE *file, iohandler_callback *callback); +struct IODescriptor *iohandler_add(int sockfd, enum IOType type, struct timeval *timeout, iohandler_callback *callback); struct IODescriptor *iohandler_timer(struct timeval timeout, iohandler_callback *callback); struct IODescriptor *iohandler_connect(const char *hostname, unsigned int port, int ssl, const char *bind, iohandler_callback *callback); struct IODescriptor *iohandler_listen(const char *hostname, unsigned int port, int ssl, iohandler_callback *callback); @@ -91,6 +89,7 @@ void iohandler_send(struct IODescriptor *iofd, const char *data, size_t datalen) void iohandler_printf(struct IODescriptor *iofd, const char *text, ...); void iohandler_close(struct IODescriptor *iofd); void iohandler_update(struct IODescriptor *iofd); +void iohandler_set_timeout(struct IODescriptor *iofd, struct timeval *timeout); void iohandler_poll(); diff --git a/src/UserClient.c b/src/UserClient.c index 5cc0834..1b07506 100644 --- a/src/UserClient.c +++ b/src/UserClient.c @@ -27,7 +27,7 @@ static struct UserClient *userclients = NULL; void userclient_accepted(struct ServerSocket *server, int sockfd) { struct UserClient *client; - struct IODescriptor *iofd = iohandler_add(sockfd, IOTYPE_CLIENT, userclient_callback); + struct IODescriptor *iofd = iohandler_add(sockfd, IOTYPE_CLIENT, NULL, userclient_callback); if(!iofd) return; iofd->state = IO_CONNECTED; iofd->read_lines = 1; diff --git a/src/UserSession.c b/src/UserSession.c index 644332f..66dd2cf 100644 --- a/src/UserSession.c +++ b/src/UserSession.c @@ -142,14 +142,16 @@ void usersession_login(struct UserLogin *login) { userclient_login_failed(login, "Login Script error."); return; } - struct IODescriptor *iofd = iohandler_add(fp, IOTYPE_CLIENT, usersession_login_callback); + struct IODescriptor *iofd = iohandler_add(fp, IOTYPE_CLIENT, NULL, usersession_login_callback); if(iofd) { iofd->read_lines = 1; iofd->state = IO_CONNECTED; int timeout = get_int_field("auth.external.timeout"); if(timeout) { - gettimeofday(&iofd->timeout, NULL); - iofd->timeout.tv_sec += timeout; + struct timeval tv_timeout; + gettimeofday(&tv_timeout, NULL); + tv_timeout.tv_sec += timeout; + iohandler_set_timeout(iofd, &tv_timeout); } iofd->data = login; login->login_iofd = iofd; diff --git a/src/overall.h b/src/overall.h index edf7157..8c2d527 100644 --- a/src/overall.h +++ b/src/overall.h @@ -46,6 +46,7 @@ #include #include #include +#include #endif #include #include @@ -84,7 +85,4 @@ #define COMPILER "Unknown" #endif -#define timeval_is_bigger(x,y) ((x->tv_sec > y->tv_sec) || (x->tv_sec == y->tv_sec && x->tv_usec > y->tv_usec)) -#define timeval_is_smaler(x,y) ((x->tv_sec < y->tv_sec) || (x->tv_sec == y->tv_sec && x->tv_usec < y->tv_usec)) - #endif -- 2.20.1