From: pk910 Date: Wed, 22 Aug 2012 00:25:23 +0000 (+0200) Subject: Merge branch 'master' into IOMultiplexer X-Git-Tag: v5.5~9^2~12 X-Git-Url: http://git.pk910.de/?p=NeonServV5.git;a=commitdiff_plain;h=291f15058a67b95bdfe6f6861cdfcb8ffd626149;hp=cee5525a5937307077e367dfa0f9df85f7f0b817 Merge branch 'master' into IOMultiplexer Conflicts: src/signal.c --- diff --git a/Makefile.am b/Makefile.am index f1cf173..14de200 100644 --- a/Makefile.am +++ b/Makefile.am @@ -172,6 +172,12 @@ libNeonFun_la_LDFLAGS = -module -rpath /nowhere -avoid-version -no-undefined libNeonFun_la_LIBADD = $(MYSQL_LIBS) neonserv_SOURCES = src/version.c \ + src/IOEngine_epoll.c \ + src/IOEngine_kevent.c \ + src/IOEngine_select.c \ + src/IOEngine_win32.c \ + src/IOHandler.c \ + src/IOHandler_SSL.c \ src/EventLogger.c \ src/IRCEvents.c \ src/main.c \ @@ -179,7 +185,6 @@ neonserv_SOURCES = src/version.c \ src/ChanNode.c \ src/IRCParser.c \ src/ClientSocket.c \ - src/ssl.c \ src/UserNode.c \ src/ChanUser.c \ src/ModeNode.c \ diff --git a/configure.ac b/configure.ac index 4ba0fac..6a52e3c 100644 --- a/configure.ac +++ b/configure.ac @@ -29,26 +29,21 @@ AC_ARG_WITH([mysql-lib], [MYSQL_LIBS='-lmysqlclient']) AC_SUBST([MYSQL_LIBS]) -AC_ARG_WITH([winsock], - [AS_HELP_STRING([--with-winsock], [use winsock (WIN32 systems)])], - [SYSTEM_LIBS='-lws2_32'], - [SYSTEM_LIBS='-ldl']) -AC_SUBST([SYSTEM_LIBS]) +AC_CHECK_LIB([ws2_32], [_head_libws2_32_a], [LIBS="$LIBS -lws2_32"]) +AC_CHECK_LIB([dl], [dlopen], [LIBS="$LIBS -ldl"]) +AC_CHECK_LIB([pthread], [pthread_create], [LIBS="$LIBS -lpthread"]) +AC_CHECK_LIB([ssl], [SSL_read], [LIBS="$LIBS -lssl"]) +AC_CHECK_LIB([crypto], [X509_new], [LIBS="$LIBS -lcrypto"]) do_have_ssl="no"; AC_CHECK_LIB(ssl, SSL_read, [ AC_CHECK_LIB(crypto, X509_new, [ AC_CHECK_HEADERS(openssl/ssl.h openssl/err.h openssl/rand.h, [ - do_have_ssl="yes"; + AC_DEFINE([HAVE_SSL], 1, [Define if you are using SSL]) ]) ]) ]) -if test x"$do_have_ssl" = xyes; then - LIBS="$LIBS -lssl -lcrypto" - AC_DEFINE([HAVE_SSL], 1, [Define if you are using SSL]) -fi - do_have_threads="no"; AC_CHECK_LIB(pthread, pthread_create, [ AC_CHECK_HEADERS(pthread.h, [ @@ -81,7 +76,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 pthread.h sys/epoll.h sys/kevent.h openssl/ssl.h openssl/err.h openssl/rand.h]) # Checks for typedefs, structures, and compiler characteristics. diff --git a/src/ClientSocket.c b/src/ClientSocket.c index cdf76f4..13f8b6b 100644 --- a/src/ClientSocket.c +++ b/src/ClientSocket.c @@ -21,9 +21,9 @@ #include "IRCQueue.h" #include "WHOHandler.h" #include "HandleInfoHandler.h" -#include "ssl.h" #include "ConfigParser.h" #include "version.h" +#include "IOHandler.h" struct socket_list { struct ClientSocket *data; @@ -43,7 +43,8 @@ struct ParseOrder *parse_order = NULL; //the magic list :P static struct socket_list *sockets = NULL; -static char buffer[BUF_SIZ]; + +static IOHANDLER_CALLBACK(socket_callback); void init_sockets() { THREAD_MUTEX_INIT(synchronized); @@ -61,11 +62,10 @@ void init_sockets() { struct ClientSocket* create_socket(char *host, int port, char *bindto, char *pass, char *nick, char *ident, char *realname) { struct ClientSocket *client = malloc(sizeof(*client)); - if (!client) - { - perror("malloc() failed"); + if (!client) { return NULL; } + client->iofd = NULL; client->host = strdup(host); client->port = port; client->bind = (bindto ? strdup(bindto) : NULL); @@ -76,7 +76,6 @@ struct ClientSocket* create_socket(char *host, int port, char *bindto, char *pas client->user = NULL; client->network_name = NULL; client->flags = 0; - client->bufferpos = 0; client->traffic_in = 0; client->traffic_out = 0; client->connection_time = 0; @@ -94,285 +93,73 @@ struct ClientSocket* create_socket(char *host, int port, char *bindto, char *pas return client; } -static int _connect_socket(struct ClientSocket *client); - -int connect_socket(struct ClientSocket *client) { - SYNCHRONIZE(synchronized); - int ret = _connect_socket(client); - DESYNCHRONIZE(synchronized); - return ret; -} - -#ifndef WIN32 -static int _connect_socket(struct ClientSocket *client) { - if((client->flags & SOCKET_FLAG_CONNECTED)) return 1; - client->connection_time = time(0); - int sock; - - struct addrinfo hints, *res; - struct sockaddr_in *ip4 = NULL; - struct sockaddr_in6 *ip6 = NULL; - memset (&hints, 0, sizeof (hints)); - hints.ai_family = PF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags |= AI_CANONNAME; - if (getaddrinfo (client->host, NULL, &hints, &res)) { - return 0; - } - while (res) { - switch (res->ai_family) { - case AF_INET: - ip4 = (struct sockaddr_in *) res->ai_addr; - break; - case AF_INET6: - ip6 = (struct sockaddr_in6 *) res->ai_addr; - break; - } - res = res->ai_next; - } - - if(ip6) { - sock = socket(AF_INET6, SOCK_STREAM, 0); - if(sock == -1) { - perror("socket() failed"); - return 0; - } - - ip6->sin6_family = AF_INET6; - ip6->sin6_port = htons(client->port); - - struct sockaddr_in6 *ip6vhost = NULL; - if (client->bind && !getaddrinfo(client->bind, NULL, &hints, &res)) { - while (res) { - switch (res->ai_family) { - case AF_INET6: - ip6vhost = (struct sockaddr_in6 *) res->ai_addr; - break; - } - res = res->ai_next; - } - } - if(ip6vhost) { - ip6vhost->sin6_family = AF_INET6; - ip6vhost->sin6_port = htons(0); - bind(sock, (struct sockaddr*)ip6vhost, sizeof(*ip6vhost)); - } - - if (connect(sock, (struct sockaddr*)ip6, sizeof(*ip6)) == -1) { - perror("connect() failed"); - return 0; - } - - } else if(ip4) { - sock = socket(AF_INET, SOCK_STREAM, 0); - if(sock == -1) { - perror("socket() failed"); - return 0; - } - - ip4->sin_family = AF_INET; - ip4->sin_port = htons(client->port); - - struct sockaddr_in *ip4vhost = NULL; - if (client->bind && !getaddrinfo(client->bind, NULL, &hints, &res)) { - while (res) { - switch (res->ai_family) { - case AF_INET: - ip4vhost = (struct sockaddr_in *) res->ai_addr; - break; - } - res = res->ai_next; - } - } - if(ip4vhost) { - ip4vhost->sin_family = AF_INET; - ip4vhost->sin_port = htons(0); - bind(sock, (struct sockaddr*)ip4vhost, sizeof(*ip4vhost)); - } - - if (connect(sock, (struct sockaddr*)ip4, sizeof(*ip4)) == -1) { - perror("connect() failed"); - return 0; - } - - } else - return 0; - - if(get_int_field("Sockets.NoDelay")) { - int flag = 1; - if(setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)) == -1) { - perror("setsockopt() failed"); - return 0; - } - } - - client->sock = sock; - client->flags |= SOCKET_FLAG_CONNECTED | SOCKET_FLAG_RECONNECT; - - if(client->flags & SOCKET_FLAG_SSL) { - ssl_connect(client); - client->flags |= SOCKET_FLAG_HAVE_SSL; - } else - client->flags &= ~SOCKET_FLAG_HAVE_SSL; - - //send the IRC Headers - char sendBuf[512]; - int len; - - if(client->pass && strcmp(client->pass, "")) { - len = sprintf(sendBuf, "PASS :%s\n", client->pass); - write_socket(client, sendBuf, len); - } - len = sprintf(sendBuf, "USER %s 0 0 :%s\n", client->ident, client->realname); - write_socket(client, sendBuf, len); - len = sprintf(sendBuf, "NICK %s\n", client->nick); - write_socket(client, sendBuf, len); - - return 1; +void connect_socket(struct ClientSocket *client) { + client->iofd = iohandler_connect(client->host, client->port, ((client->flags & SOCKET_FLAG_SSL) ? 1 : 0), client->bind, socket_callback); + client->iofd->data = client; } -#else -static int _connect_socket(struct ClientSocket *client) { - if((client->flags & SOCKET_FLAG_CONNECTED)) return 1; - client->connection_time = time(0); - struct hostent *host; - struct sockaddr_in addr; - int sock; - addr.sin_addr.s_addr = inet_addr(client->host); - if (addr.sin_addr.s_addr == INADDR_NONE) { - host = gethostbyname(client->host); - if(!host) { - return SOCKET_ERROR; - } - memcpy(&(addr.sin_addr), host->h_addr_list[0], 4); - } - sock = socket(PF_INET, SOCK_STREAM, 0); - if (sock == -1) - { - perror("socket() failed"); - return 0; - } - - addr.sin_port = htons(client->port); - addr.sin_family = AF_INET; - - if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == -1) - { - perror("connect() failed"); - return 0; - } - - client->sock = sock; - client->flags |= SOCKET_FLAG_CONNECTED | SOCKET_FLAG_RECONNECT; - - - if(client->flags & SOCKET_FLAG_SSL) { - ssl_connect(client); - client->flags |= SOCKET_FLAG_HAVE_SSL; - } else - client->flags &= ~SOCKET_FLAG_HAVE_SSL; - - //send the IRC Headers - char sendBuf[512]; - int len; - - if(client->pass && strcmp(client->pass, "")) { - len = sprintf(sendBuf, "PASS :%s\n", client->pass); - write_socket(client, sendBuf, len); - } - len = sprintf(sendBuf, "USER %s 0 0 :%s\n", client->ident, client->realname); - write_socket(client, sendBuf, len); - len = sprintf(sendBuf, "NICK %s\n", client->nick); - write_socket(client, sendBuf, len); - - return 1; -} -#endif int close_socket(struct ClientSocket *client) { if(client == NULL) return 0; if((client->flags & SOCKET_FLAG_CONNECTED)) { - char quitbuf[MAXLEN]; - int quitlen = sprintf(quitbuf, "QUIT :[NeonServ %s.%d] disconnect requested.\n", NEONSERV_VERSION, patchlevel); - write_socket_force(client, quitbuf, quitlen); - } - client->flags &= ~(SOCKET_FLAG_READY | SOCKET_FLAG_RECONNECT); - client->flags |= SOCKET_FLAG_QUITTED | SOCKET_FLAG_DEAD; - return 1; -} - -int disconnect_socket(struct ClientSocket *client) { - if(client == NULL) return 0; - if((client->flags & SOCKET_FLAG_CONNECTED)) { - char quitbuf[MAXLEN]; - int quitlen = sprintf(quitbuf, "QUIT :[NeonServ %s.%d] disconnect requested.\n", NEONSERV_VERSION, patchlevel); - write_socket_force(client, quitbuf, quitlen); - } - client->flags &= ~(SOCKET_FLAG_READY | SOCKET_FLAG_RECONNECT); - client->flags |= SOCKET_FLAG_QUITTED; - return 1; -} - -static void destroy_socket(struct ClientSocket *client, int free_socket) { - SYNCHRONIZE(synchronized); - if((client->flags & SOCKET_FLAG_CONNECTED)) { - close(client->sock); + iohandler_printf(client->iofd, "QUIT :[NeonServ %s.%d] disconnect requested.\n", NEONSERV_VERSION, patchlevel); + bot_disconnect(client); + + iohandler_close(client->iofd); + client->iofd = NULL; } - if(client->flags & SOCKET_FLAG_HAVE_SSL) - ssl_disconnect(client); + client->flags &= ~(SOCKET_FLAG_READY | SOCKET_FLAG_CONNECTED); if(client->queue) queue_destroy(client); if(client->whoqueue_first) clear_whoqueue(client); if(client->handleinfo_first) clear_handleinfoqueue(client); - client->flags &= ~(SOCKET_FLAG_CONNECTED | SOCKET_FLAG_READY | SOCKET_FLAG_HAVE_SSL); - if(free_socket) { - struct ClientSocket *sock, *last_sock = NULL; - for (sock = sockets->data; sock; sock = sock->next) { - if(sock == client) { - if(last_sock) - last_sock->next = sock->next; - else - sockets->data = sock->next; - sockets->count--; - break; - } else - last_sock = sock; - } - free(client->host); - if(client->bind) - free(client->bind); - if(client->pass) - free(client->pass); - if(client->network_name) - free(client->network_name); - free(client); - } else if(client->flags & SOCKET_FLAG_FAST_JUMP) { - client->flags &= ~SOCKET_FLAG_FAST_JUMP; - connect_socket(client); + return 1; +} + +int destroy_socket(struct ClientSocket *client) { + if(client == NULL) return 0; + close_socket(client); + SYNCHRONIZE(synchronized); + struct ClientSocket *sock, *last_sock = NULL; + for (sock = sockets->data; sock; sock = sock->next) { + if(sock == client) { + if(last_sock) + last_sock->next = sock->next; + else + sockets->data = sock->next; + sockets->count--; + break; + } else + last_sock = sock; } + free(client->host); + if(client->bind) + free(client->bind); + if(client->pass) + free(client->pass); + if(client->network_name) + free(client->network_name); + if(client->iofd) //reconnect timer? + iohandler_close(client->iofd); + free(client); DESYNCHRONIZE(synchronized); + return 1; } int write_socket_force(struct ClientSocket *client, char* msg, int len) { + if(!(client && (client->flags & SOCKET_FLAG_CONNECTED))) return 0; SYNCHRONIZE(synchronized); #ifdef HAVE_THREADS putlog(LOGLEVEL_RAW, "[%d send %d] %s", getCurrentThreadID(), len, msg); #else putlog(LOGLEVEL_RAW, "[send %d] %s", len, msg); #endif - int ret = 1; - if(!(client->flags & SOCKET_FLAG_HAVE_SSL) || ssl_write(client, msg, len) == -2) { - #ifdef WIN32 - ret = send(client->sock, msg, len, 0); - #else - ret = write(client->sock, msg, len); - #endif - } + iohandler_send(client->iofd, msg, len); client->traffic_out += len; DESYNCHRONIZE(synchronized); - return ret; + return 1; } int write_socket(struct ClientSocket *client, char* msg, int len) { @@ -426,120 +213,47 @@ int clientsocket_parseorder_top(unsigned int tid) { } #endif -int socket_loop(int timeout_seconds) { - if(sockets == NULL) return 0; - int is_synchronized = 1; - SYNCHRONIZE(synchronized_recv); - fd_set fds; - struct timeval timeout; - struct ClientSocket *sock, *next; - int ret = 0, bytes, i; - - FD_ZERO(&fds); - for (sock = sockets->data; sock; sock = sock->next) { - if(!(sock->flags & SOCKET_FLAG_CONNECTED)) continue; //skip disconnected sockets - FD_SET(sock->sock, &fds); - if(sock->sock > ret) - ret = sock->sock; - } - timeout.tv_sec = timeout_seconds; - timeout.tv_usec = 0; - ret = select(ret + 1, &fds, NULL, NULL, &timeout); - for (sock = sockets->data; sock; sock = next) { - next = sock->next; - if((sock->flags & (SOCKET_FLAG_CONNECTED | SOCKET_FLAG_QUITTED)) == SOCKET_FLAG_CONNECTED && FD_ISSET(sock->sock, &fds)) { - if(sock->bufferpos != 0) { - if(!(sock->flags & SOCKET_FLAG_HAVE_SSL) || (bytes = ssl_read(sock, buffer, sizeof(buffer))) == -2) { - #ifdef WIN32 - bytes = recv(sock->sock, buffer, sizeof(buffer), 0); - #else - bytes = read(sock->sock, buffer, sizeof(buffer)); - #endif - } - if(bytes > 0) { - for(i = 0; i < bytes; i++) { - if(sock->bufferpos + i == BUF_SIZ*2) break; //buffer overflow - sock->buffer[sock->bufferpos + i] = buffer[i]; - } - sock->bufferpos += i; - } - } else { - if(!(sock->flags & SOCKET_FLAG_HAVE_SSL) || (bytes = ssl_read(sock, sock->buffer, sizeof(sock->buffer))) == -2) { - #ifdef WIN32 - bytes = recv(sock->sock, sock->buffer, sizeof(sock->buffer), 0); - #else - bytes = read(sock->sock, sock->buffer, sizeof(sock->buffer)); - #endif - } - if(bytes > 0) - sock->bufferpos = bytes; - } - if(bytes <= 0) { - //error - sock->flags |= SOCKET_FLAG_QUITTED; - } else { - sock->traffic_in += bytes; - #ifdef HAVE_THREADS - char linesbuf[BUF_SIZ*2]; - strcpy(linesbuf, sock->buffer); - int used = 0; - for(i = 0; i < sock->bufferpos; i++) { - if(sock->buffer[i] == '\n') { - used = i+1; - } - } - if(used == sock->bufferpos + 1) { - //used all bytes so just reset the bufferpos - sock->bufferpos = 0; - } else { - for(i = 0; i < sock->bufferpos - used; i++) { - sock->buffer[i] = sock->buffer[i+used]; - } - sock->bufferpos -= used; - } - is_synchronized = 0; - unsigned int tid = (unsigned int) pthread_self_tid(); - clientsocket_start_of_recv(tid); - DESYNCHRONIZE(synchronized_recv); - parse_lines(sock, linesbuf, used); - clientsocket_end_of_recv(tid); - #else - int used = parse_lines(sock, sock->buffer, sock->bufferpos); - if(used == sock->bufferpos + 1) { - //used all bytes so just reset the bufferpos - sock->bufferpos = 0; - } else { - for(i = 0; i < sock->bufferpos - used; i++) { - sock->buffer[i] = sock->buffer[i+used]; - } - sock->bufferpos -= used; - } - is_synchronized = 0; - DESYNCHRONIZE(synchronized_recv); - #endif - #ifdef HAVE_THREADS - FD_ZERO(&fds); //zero out all other pending sockets here (we have other threads receiving from them) - #endif - } - } else if((sock->flags & (SOCKET_FLAG_CONNECTED | SOCKET_FLAG_RECONNECT)) == SOCKET_FLAG_RECONNECT) { - if(time(0) - sock->connection_time >= SOCKET_RECONNECT_TIME) { - connect_socket(sock); - } - } - if((sock->flags & SOCKET_FLAG_QUITTED)) { - sock->flags &= ~SOCKET_FLAG_QUITTED; - destroy_socket(sock, (sock->flags & SOCKET_FLAG_DEAD)); +static IOHANDLER_CALLBACK(socket_callback) { + struct ClientSocket *client = event->iofd->data; + unsigned int tid; + switch(event->type) { + case IOEVENT_CONNECTED: + client->flags |= SOCKET_FLAG_CONNECTED; + if(client->pass && strcmp(client->pass, "")) + iohandler_printf(event->iofd, "PASS :%s", client->pass); + iohandler_printf(event->iofd, "USER %s 0 0 :%s", client->ident, client->realname); + iohandler_printf(event->iofd, "NICK %s", client->nick); + break; + case IOEVENT_NOTCONNECTED: + case IOEVENT_CLOSED: + close_socket(client); + if(client->flags & SOCKET_FLAG_RECONNECT) { + struct timeval timeout; + gettimeofday(&timeout, NULL); + timeout.tv_sec += SOCKET_RECONNECT_TIME; + client->iofd = iohandler_timer(timeout, socket_callback); + client->iofd->data = client; } + break; + case IOEVENT_TIMEOUT: //reconnect timer + connect_socket(client); + break; + case IOEVENT_RECV: + #ifdef HAVE_THREADS + tid = (unsigned int) pthread_self_tid(); + clientsocket_start_of_recv(tid); + #endif + parse_line(client, event->data.recv_str); + #ifdef HAVE_THREADS + clientsocket_end_of_recv(tid); + #endif + break; + default: + break; } - if(is_synchronized) { - DESYNCHRONIZE(synchronized_recv); - } - return 1; } -void -putsock(struct ClientSocket *client, const char *text, ...) -{ +void putsock(struct ClientSocket *client, const char *text, ...) { va_list arg_list; char sendBuf[MAXLEN]; int pos; @@ -569,20 +283,7 @@ void free_sockets() { struct ClientSocket *client, *next; for (client = sockets->data; client; client = next) { next = client->next; - if((client->flags & SOCKET_FLAG_CONNECTED)) - close(client->sock); - if(client->flags & SOCKET_FLAG_HAVE_SSL) - ssl_disconnect(client); - if(client->queue) - queue_destroy(client); - free(client->host); - if(client->bind) - free(client->bind); - if(client->pass) - free(client->pass); - if(client->network_name) - free(client->network_name); - free(client); + destroy_socket(client); } free(sockets); sockets = NULL; diff --git a/src/ClientSocket.h b/src/ClientSocket.h index ddbb60f..b8db110 100644 --- a/src/ClientSocket.h +++ b/src/ClientSocket.h @@ -19,15 +19,13 @@ #include "main.h" -#define SOCKET_FLAG_DEAD 0x01 #define SOCKET_FLAG_CONNECTED 0x02 #define SOCKET_FLAG_READY 0x04 #define SOCKET_FLAG_PREFERRED 0x08 /* prefered bot to send datas to the IRC World (NOTICE's WHO's etc pp) */ #define SOCKET_FLAG_USE_QUEUE 0x10 #define SOCKET_FLAG_RECONNECT 0x20 #define SOCKET_FLAG_SSL 0x40 -#define SOCKET_FLAG_HAVE_SSL 0x80 -#define SOCKET_FLAG_QUITTED 0x100 + #define SOCKET_FLAG_FAST_JUMP 0x200 #define SOCKET_FLAG_SILENT 0x400 #define SOCKET_FLAG_CHANGENICK 0x800 @@ -43,13 +41,11 @@ struct UserNode; struct trigger_cache; -struct SSLConnection; +struct IODescriptor; struct ClientSocket { - int sock; + struct IODescriptor *iofd; unsigned int flags; - char buffer[BUF_SIZ*2]; //we need to store up to 2 full commands at once - unsigned int bufferpos; char *host; int port; char *bind; @@ -62,7 +58,6 @@ struct ClientSocket { unsigned long traffic_in; unsigned long traffic_out; time_t connection_time; - struct SSLConnection *sslconn; struct BotQueue *queue; @@ -86,15 +81,14 @@ struct ClientSocket { #ifndef DND_FUNCTIONS /* MODULAR ACCESSIBLE */ struct ClientSocket* create_socket(char *host, int port, char *bindto, char *pass, char *nick, char *ident, char *realname); -/* MODULAR ACCESSIBLE */ int connect_socket(struct ClientSocket *client); +/* MODULAR ACCESSIBLE */ void connect_socket(struct ClientSocket *client); /* MODULAR ACCESSIBLE */ int close_socket(struct ClientSocket *client); -/* MODULAR ACCESSIBLE */ int disconnect_socket(struct ClientSocket *client); +/* MODULAR ACCESSIBLE */ int destroy_socket(struct ClientSocket *client); int write_socket_force(struct ClientSocket *client, char* msg, int len); /* MODULAR ACCESSIBLE */ int write_socket(struct ClientSocket *client, char* msg, int len); #ifdef HAVE_THREADS int clientsocket_parseorder_top(unsigned int tid); #endif -int socket_loop(int timeout_seconds); /* MODULAR ACCESSIBLE */ void putsock(struct ClientSocket *client, const char *text, ...) PRINTF_LIKE(2, 3); /* MODULAR ACCESSIBLE */ struct ClientSocket* getBots(int flags, struct ClientSocket* last_bot); void init_sockets(); diff --git a/src/IOEngine.h b/src/IOEngine.h new file mode 100644 index 0000000..f64078e --- /dev/null +++ b/src/IOEngine.h @@ -0,0 +1,66 @@ +/* IOEngine.h - IOMultiplexer + * Copyright (C) 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 . + */ +#ifndef _IOEngine_h +#define _IOEngine_h +#include "IOHandler.h" + +#ifdef HAVE_PTHREAD_H +#include +#define IOTHREAD_MUTEX_INIT(var) { \ + pthread_mutexattr_t mutex_attr; \ + pthread_mutexattr_init(&mutex_attr);\ + pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_RECURSIVE_NP);\ + pthread_mutex_init(&var, &mutex_attr); \ +} +#define IOSYNCHRONIZE(var) pthread_mutex_lock(&var) +#define IODESYNCHRONIZE(var) pthread_mutex_unlock(&var) +#else +#define IOTHREAD_MUTEX_INIT(var) +#define IOSYNCHRONIZE(var) +#define IODESYNCHRONIZE(var) +#endif + +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)) + +extern struct IODescriptor *first_descriptor; +extern struct IODescriptor *timer_priority; + +struct IOEngine { + const char *name; + int (*init)(void); + void (*add)(struct IODescriptor *iofd); + void (*remove)(struct IODescriptor *iofd); + void (*update)(struct IODescriptor *iofd); + void (*loop)(struct timeval *timeout); + void (*cleanup)(void); +}; + +#define iohandler_wants_writes(IOFD) ((IOFD->writebuf.bufpos && !IOFD->ssl_hs_read) || IOFD->state == IO_CONNECTING || IOFD->ssl_hs_write) + +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..19c6227 --- /dev/null +++ b/src/IOEngine_epoll.c @@ -0,0 +1,154 @@ +/* IOEngine_epoll.c - IOMultiplexer + * Copyright (C) 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" + +#ifdef HAVE_SYS_EPOLL_H +#include +#include +#include +#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..1ebb801 --- /dev/null +++ b/src/IOEngine_kevent.c @@ -0,0 +1,165 @@ +/* IOengine_kevent.c - IOMultiplexer + * Copyright (C) 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" + +#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 new file mode 100644 index 0000000..8c25ba6 --- /dev/null +++ b/src/IOEngine_select.c @@ -0,0 +1,177 @@ +/* IOEngine_select.c - IOMultiplexer + * Copyright (C) 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 +#ifdef WIN32 +#define _WIN32_WINNT 0x501 +#include +#include +#else +#include +#include +#endif + +static int engine_select_init() { + /* empty */ + return 1; +} + +static void engine_select_add(struct IODescriptor *iofd) { + #ifdef WIN32 + if(iofd->type == IOTYPE_STDIN) + SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE),ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT); + #endif + /* empty */ +} + +static void engine_select_remove(struct IODescriptor *iofd) { + /* empty */ +} + +static void engine_select_update(struct IODescriptor *iofd) { + /* empty */ +} + +static void engine_select_loop(struct timeval *timeout) { + fd_set read_fds; + fd_set write_fds; + unsigned int fds_size = 0; + struct IODescriptor *iofd, *tmp_iofd; + struct timeval now, tdiff; + int select_result; + + gettimeofday(&now, NULL); + + //clear fds + FD_ZERO(&read_fds); + FD_ZERO(&write_fds); + + for(iofd = first_descriptor; iofd; iofd = tmp_iofd) { + tmp_iofd = iofd->next; + if(iofd->type == IOTYPE_STDIN) { + #ifdef WIN32 + //WIN32 doesn't support stdin within select + //just try to read the single events from the console + DWORD dwRead; + INPUT_RECORD inRecords[128]; + unsigned int i; + int read_bytes = 0; + GetNumberOfConsoleInputEvents(GetStdHandle(STD_INPUT_HANDLE), &dwRead); + if(dwRead) + ReadConsoleInput(GetStdHandle(STD_INPUT_HANDLE), &inRecords[0], 128, &dwRead); + for (i = 0; i < dwRead; ++i) { + if (inRecords[i].EventType == KEY_EVENT) { + const char c = inRecords[i].Event.KeyEvent.uChar.AsciiChar; + if (inRecords[i].Event.KeyEvent.bKeyDown && c != 0) { + iofd->readbuf.buffer[iofd->readbuf.bufpos + read_bytes] = c; + read_bytes++; + } + } + } + if(read_bytes) + iohandler_events(iofd, read_bytes, 0); + if(read_bytes >= 128) { + timeout->tv_sec = 0; + timeout->tv_usec = 1; + //minimal timeout + } else { + timeout->tv_sec = 0; + timeout->tv_usec = 100000; + } + #else + if(iofd->fd > fds_size) + fds_size = iofd->fd; + FD_SET(iofd->fd, &read_fds); + #endif + } + else if(iofd->type == IOTYPE_SERVER || iofd->type == IOTYPE_CLIENT) { + if(iofd->fd > fds_size) + fds_size = iofd->fd; + FD_SET(iofd->fd, &read_fds); + if(iohandler_wants_writes(iofd)) + FD_SET(iofd->fd, &write_fds); + } + } + + 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 + select_result = select(fds_size + 1, &read_fds, &write_fds, NULL, timeout); + + if (select_result < 0) { + if (errno != EINTR) { + iohandler_log(IOLOG_FATAL, "select() failed with errno %d %d: %s", select_result, errno, strerror(errno)); + return; + } + } + + gettimeofday(&now, NULL); + + //check all descriptors + for(iofd = first_descriptor; iofd; iofd = tmp_iofd) { + tmp_iofd = iofd->next; + if(iofd->type == IOTYPE_SERVER || iofd->type == IOTYPE_CLIENT || iofd->type == IOTYPE_STDIN) { + if(FD_ISSET(iofd->fd, &read_fds) || FD_ISSET(iofd->fd, &write_fds)) { + iohandler_events(iofd, FD_ISSET(iofd->fd, &read_fds), FD_ISSET(iofd->fd, &write_fds)); + 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() { + /* empty */ +} + +struct IOEngine engine_select = { + .name = "select", + .init = engine_select_init, + .add = engine_select_add, + .remove = engine_select_remove, + .update = engine_select_update, + .loop = engine_select_loop, + .cleanup = engine_select_cleanup, +}; diff --git a/src/IOEngine_win32.c b/src/IOEngine_win32.c new file mode 100644 index 0000000..bce6fc1 --- /dev/null +++ b/src/IOEngine_win32.c @@ -0,0 +1,275 @@ +/* IOEngine_win32.c - IOMultiplexer + * Copyright (C) 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" + +#ifdef WIN32 + +#define _WIN32_WINNT 0x501 +#include +#include + +/* This is massively kludgy. Unfortunately, the only performant I/O + * multiplexer with halfway decent semantics under Windows is + * WSAAsyncSelect() -- which requires a window that can receive + * messages. + * + * So ioset_win32_init() creates a hidden window and sets it up for + * asynchronous socket notifications. + */ + +#define IDT_TIMER1 1000 +#define IDT_TIMER2 1001 +#define IDT_SOCKET 1002 + +static HWND ioset_window; + +static struct IODescriptor *engine_win32_get_iofd(int fd) { + struct IODescriptor *iofd; + for(iofd = first_descriptor; iofd; iofd = iofd->next) { + if(iofd->fd == fd) + return iofd; + } + return NULL; +} + +static LRESULT CALLBACK engine_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + struct IODescriptor *iofd; + int events; + struct timeval now, tdiff; + + gettimeofday(&now, NULL); + + if (hWnd == ioset_window) switch (uMsg) + { + case IDT_TIMER1: + return 0; + case IDT_TIMER2: + //User Timer + 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; + } + return 0; + case IDT_SOCKET: + iofd = engine_win32_get_iofd(wParam); + events = WSAGETSELECTEVENT(lParam); + + iohandler_events(iofd, (events & (FD_READ | FD_ACCEPT | FD_CLOSE)) != 0, (events & (FD_WRITE | FD_CONNECT)) != 0); + return 0; + case WM_QUIT: + return 0; + } + return DefWindowProc(hWnd, uMsg, wParam, lParam); +} + +static int engine_win32_init() { + WNDCLASSEX wcx; + HINSTANCE hinst; + WSADATA wsadata; + + // Start Windows Sockets. + if (WSAStartup(MAKEWORD(2, 0), &wsadata)) { + iohandler_log(IOLOG_FATAL, "Unable to start Windows Sockets"); + return 0; + } + + // Get Windows HINSTANCE. + hinst = GetModuleHandle(NULL); + + // Describe and register a window class. + memset(&wcx, 0, sizeof(wcx)); + wcx.cbSize = sizeof(wcx); + wcx.lpfnWndProc = engine_win32_wndproc; + wcx.hInstance = hinst; + wcx.lpszClassName = "IOMultiplexerMainWindow"; + if (!RegisterClassEx(&wcx)) + return 0; + + ioset_window = CreateWindow("IOMultiplexerMainWindow", "IOMultiplexer", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinst, NULL); + if (!ioset_window) + return 0; + return 1; +} + +static long engine_win32_events(struct IODescriptor *iofd) { + switch (iofd->state) { + case IO_CLOSED: + return 0; + case IO_LISTENING: + return FD_ACCEPT; + case IO_CONNECTING: + return FD_CONNECT; + case IO_CONNECTED: + case IO_SSLWAIT: + return FD_READ | FD_CLOSE | (iohandler_wants_writes(iofd) ? FD_WRITE : 0); + } + return 0; +} + +static void engine_win32_update(struct IODescriptor *iofd) { + long events; + + if(iofd->type == IOTYPE_STDIN) + return; + + events = engine_win32_events(iofd); + WSAAsyncSelect(iofd->fd, ioset_window, IDT_SOCKET, events); +} + +static void engine_win32_add(struct IODescriptor *iofd) { + if(iofd->type == IOTYPE_STDIN) + return; + + engine_win32_update(iofd); +} + +static void engine_win32_remove(struct IODescriptor *iofd) { + unsigned long ulong; + + if(iofd->type == IOTYPE_STDIN) + return; + + WSAAsyncSelect(iofd->fd, ioset_window, IDT_SOCKET, 0); + + ulong = 0; + ioctlsocket(iofd->fd, FIONBIO, &ulong); +} + +static void engine_win32_loop(struct timeval *timeout) { + MSG msg; + BOOL not_really_bool; + int msec, cmsec, sett2; + struct timeval now, tdiff; + struct IODescriptor *iofd, *tmp_iofd; + + gettimeofday(&now, NULL); + + for(iofd = first_descriptor; iofd; iofd = tmp_iofd) { + tmp_iofd = iofd->next; + if(iofd->type == IOTYPE_STDIN) { + #ifdef WIN32 + //WIN32 doesn't support stdin within select + //just try to read the single events from the console + DWORD dwRead; + INPUT_RECORD inRecords[128]; + unsigned int i; + int read_bytes = 0; + GetNumberOfConsoleInputEvents(GetStdHandle(STD_INPUT_HANDLE), &dwRead); + if(dwRead) + ReadConsoleInput(GetStdHandle(STD_INPUT_HANDLE), &inRecords[0], 128, &dwRead); + for (i = 0; i < dwRead; ++i) { + if (inRecords[i].EventType == KEY_EVENT) { + const char c = inRecords[i].Event.KeyEvent.uChar.AsciiChar; + if (inRecords[i].Event.KeyEvent.bKeyDown && c != 0) { + iofd->readbuf.buffer[iofd->readbuf.bufpos + read_bytes] = c; + read_bytes++; + } + } + } + if(read_bytes) + iohandler_events(iofd, read_bytes, 0); + if(read_bytes >= 128) { + timeout->tv_sec = 0; + timeout->tv_usec = 1; + //minimal timeout + } else { + timeout->tv_sec = 0; + timeout->tv_usec = 100000; + } + #else + if(iofd->fd > fds_size) + fds_size = iofd->fd; + FD_SET(iofd->fd, &read_fds); + #endif + } + } + + // Make sure we are woken up after the appropriate time. + msec = (timeout->tv_sec * 1000) + (timeout->tv_usec / 1000); + SetTimer(ioset_window, IDT_TIMER1, msec, NULL); + + //set additional User Timer (if ther's one) + sett2 = 0; + 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 < 1000)) { + 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 + } + cmsec = (tdiff.tv_sec * 1000) + (tdiff.tv_usec / 1000); + if(cmsec < msec) { + sett2 = 1; + msec = cmsec; + } + break; + } + if(sett2) + SetTimer(ioset_window, IDT_TIMER2, msec, NULL); + + // Do a blocking read of the message queue. + not_really_bool = GetMessage(&msg, NULL, 0, 0); + KillTimer(ioset_window, IDT_TIMER1); + if(sett2) + KillTimer(ioset_window, IDT_TIMER2); + if (not_really_bool <=0) + return; + else { + TranslateMessage(&msg); + DispatchMessage(&msg); + } +} + +static void engine_win32_cleanup() { + DestroyWindow(ioset_window); + ioset_window = NULL; + WSACleanup(); +} + +struct IOEngine engine_win32 = { + .name = "win32", + .init = engine_win32_init, + .add = engine_win32_add, + .remove = engine_win32_remove, + .update = engine_win32_update, + .loop = engine_win32_loop, + .cleanup = engine_win32_cleanup, +}; + +#else + +struct IOEngine engine_win32 = { + .name = "win32", + .init = NULL, + .add = NULL, + .remove = NULL, + .update = NULL, + .loop = NULL, + .cleanup = NULL, +}; + +#endif diff --git a/src/IOHandler.c b/src/IOHandler.c new file mode 100644 index 0000000..4f057e8 --- /dev/null +++ b/src/IOHandler.c @@ -0,0 +1,738 @@ +/* IOHandler.c - IOMultiplexer + * Copyright (C) 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 "IOHandler.h" +#include "IOEngine.h" +#include "IOHandler_SSL.h" +#include +#include +#include +#ifdef WIN32 +#define _WIN32_WINNT 0x501 +#include +#include +#include +#else +#include +#include +#include +#include +#include +#include +#include +#endif + +#define MAXLOG 1024 +iohandler_log_callback *iolog_backend = NULL; + +struct IODescriptor *first_descriptor = NULL; +struct IODescriptor *timer_priority = NULL; + +#ifdef HAVE_PTHREAD_H +static pthread_mutex_t io_thread_sync; +static pthread_mutex_t io_poll_sync; +#endif + +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'; + + if(iolog_backend) + iolog_backend(type, 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; +extern struct IOEngine engine_win32; + +struct IOEngine *engine = NULL; + +static void iohandler_init_engine() { + if(engine) return; + IOTHREAD_MUTEX_INIT(io_thread_sync); + IOTHREAD_MUTEX_INIT(io_poll_sync); + + //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 && engine_win32.init && engine_win32.init()) + engine = &engine_win32; + + 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); + iohandler_ssl_init(); +} + +static void iohandler_append(struct IODescriptor *descriptor) { + IOSYNCHRONIZE(io_thread_sync); + 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; + } + IODESYNCHRONIZE(io_thread_sync); +} + +static void iohandler_remove(struct IODescriptor *descriptor, int engine_remove) { + //remove IODescriptor from the list + IOSYNCHRONIZE(io_thread_sync); + 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); + IODESYNCHRONIZE(io_thread_sync); +} + +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) { + iohandler_log(IOLOG_ERROR, "could not allocate memory for IODescriptor in %s:%d", __FILE__, __LINE__); + return NULL; + } + descriptor->fd = (type == IOTYPE_STDIN ? fileno(stdin) : sockfd); + descriptor->type = type; + descriptor->state = (type == IOTYPE_STDIN ? IO_CONNECTED : 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; + descriptor->readbuf.buflen = IO_READ_BUFLEN; + descriptor->writebuf.buffer = malloc(IO_READ_BUFLEN + 2); + descriptor->writebuf.bufpos = 0; + descriptor->writebuf.buflen = IO_READ_BUFLEN; + } + + if(!engine) { + iohandler_init_engine(); + if(!engine) { + return NULL; + } + } + engine->add(descriptor); + + //add IODescriptor to the list + iohandler_append(descriptor); + + iohandler_log(IOLOG_DEBUG, "added custom socket descriptor (%d) as type `%s`", sockfd, iohandler_iotype_name(type)); + return descriptor; +} + +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(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) { + if(iobuf->buflen >= required) return; + char *new_buf = realloc(iobuf->buffer, required + 2); + if(new_buf) { + iobuf->buffer = new_buf; + iobuf->buflen = required; + } +} + +struct IODescriptor *iohandler_timer(struct timeval timeout, iohandler_callback *callback) { + struct IODescriptor *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; +} + +struct IODescriptor *iohandler_connect(const char *hostname, unsigned int port, int ssl, const char *bindhost, iohandler_callback *callback) { + //non-blocking connect + int sockfd, result; + struct addrinfo hints, *res; + struct sockaddr_in *ip4 = NULL; + struct sockaddr_in6 *ip6 = NULL; + size_t dstaddrlen; + struct sockaddr *dstaddr = NULL; + struct IODescriptor *descriptor; + + if(!engine) { + iohandler_init_engine(); + if(!engine) return NULL; + } + memset (&hints, 0, sizeof (hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags |= AI_CANONNAME; + if ((result = getaddrinfo (hostname, NULL, &hints, &res))) { + iohandler_log(IOLOG_ERROR, "could not resolve %s to an IP address (%d)", hostname, result); + return NULL; + } + while (res) { + switch (res->ai_family) { + case AF_INET: + ip4 = (struct sockaddr_in *) res->ai_addr; + break; + case AF_INET6: + ip6 = (struct sockaddr_in6 *) res->ai_addr; + break; + } + res = res->ai_next; + freeaddrinfo(res); + } + + if(ip6) { + sockfd = socket(AF_INET6, SOCK_STREAM, 0); + 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); + + struct sockaddr_in6 *ip6vhost = NULL; + if (bindhost && !getaddrinfo(bindhost, NULL, &hints, &res)) { + while (res) { + switch (res->ai_family) { + case AF_INET6: + ip6vhost = (struct sockaddr_in6 *) res->ai_addr; + break; + } + res = res->ai_next; + freeaddrinfo(res); + } + } + if(ip6vhost) { + ip6vhost->sin6_family = AF_INET6; + ip6vhost->sin6_port = htons(0); + bind(sockfd, (struct sockaddr*)ip6vhost, sizeof(*ip6vhost)); + } + dstaddr = (struct sockaddr*)ip6; + dstaddrlen = sizeof(*ip6); + } else if(ip4) { + sockfd = socket(AF_INET, SOCK_STREAM, 0); + 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); + + struct sockaddr_in *ip4vhost = NULL; + if (bindhost && !getaddrinfo(bindhost, NULL, &hints, &res)) { + while (res) { + switch (res->ai_family) { + case AF_INET: + ip4vhost = (struct sockaddr_in *) res->ai_addr; + break; + } + res = res->ai_next; + freeaddrinfo(res); + } + } + if(ip4vhost) { + ip4vhost->sin_family = AF_INET; + ip4vhost->sin_port = htons(0); + bind(sockfd, (struct sockaddr*)ip4vhost, sizeof(*ip4vhost)); + } + dstaddr = (struct sockaddr*)ip4; + dstaddrlen = sizeof(*ip4); + } else + return NULL; + //make sockfd unblocking +#if defined(F_GETFL) + { + 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, NULL, callback); + if(!descriptor) { + close(sockfd); + return NULL; + } + connect(sockfd, dstaddr, dstaddrlen); //returns EINPROGRESS here (nonblocking) + descriptor->state = IO_CONNECTING; + descriptor->ssl = (ssl ? 1 : 0); + descriptor->read_lines = 1; + engine->update(descriptor); + iohandler_log(IOLOG_DEBUG, "added client socket (%d) connecting to %s:%d", sockfd, hostname, port); + return descriptor; +} + +struct IODescriptor *iohandler_listen(const char *hostname, unsigned int port, iohandler_callback *callback) { + int sockfd; + struct addrinfo hints, *res; + struct sockaddr_in *ip4 = NULL; + struct sockaddr_in6 *ip6 = NULL; + struct IODescriptor *descriptor; + unsigned int opt; + + if(!engine) { + iohandler_init_engine(); + if(!engine) return NULL; + } + memset (&hints, 0, sizeof (hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags |= AI_CANONNAME; + if (getaddrinfo (hostname, NULL, &hints, &res)) { + return NULL; + } + while (res) { + switch (res->ai_family) { + case AF_INET: + ip4 = (struct sockaddr_in *) res->ai_addr; + break; + case AF_INET6: + ip6 = (struct sockaddr_in6 *) res->ai_addr; + break; + } + res = res->ai_next; + freeaddrinfo(res); + } + + if(ip6) { + sockfd = socket(AF_INET6, SOCK_STREAM, 0); + if(sockfd == -1) return NULL; + + opt = 1; + setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt)); + + ip6->sin6_family = AF_INET6; + ip6->sin6_port = htons(port); + + bind(sockfd, (struct sockaddr*)ip6, sizeof(*ip6)); + } else if(ip4) { + sockfd = socket(AF_INET, SOCK_STREAM, 0); + if(sockfd == -1) return NULL; + + opt = 1; + setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt)); + + ip4->sin_family = AF_INET; + ip4->sin_port = htons(port); + + bind(sockfd, (struct sockaddr*)ip4, sizeof(*ip4)); + } else + return NULL; + //make sockfd unblocking +#if defined(F_GETFL) + { + 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, NULL, callback); + if(!descriptor) { + close(sockfd); + return NULL; + } + 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; +} + +void iohandler_write(struct IODescriptor *iofd, const char *line) { + size_t linelen = strlen(line); + iohandler_send(iofd, line, linelen); +} + +void iohandler_send(struct IODescriptor *iofd, const char *data, size_t datalen) { + 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) { + 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; + engine->update(iofd); +} + +void iohandler_printf(struct IODescriptor *iofd, const char *text, ...) { + va_list arg_list; + char sendBuf[IO_LINE_LEN]; + int pos; + sendBuf[0] = '\0'; + va_start(arg_list, text); + pos = vsnprintf(sendBuf, IO_LINE_LEN - 2, text, arg_list); + va_end(arg_list); + if (pos < 0 || pos > (IO_LINE_LEN - 2)) pos = IO_LINE_LEN - 2; + sendBuf[pos] = '\n'; + sendBuf[pos+1] = '\0'; + iohandler_send(iofd, sendBuf, pos+1); +} + +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; + if(iofd->ssl_active) + res = iohandler_ssl_write(iofd, iofd->writebuf.buffer, iofd->writebuf.bufpos); + else + res = send(iofd->fd, iofd->writebuf.buffer, iofd->writebuf.bufpos, 0); + if(res < 0) { + if (errno != EAGAIN) { + iohandler_log(IOLOG_ERROR, "could not write to socket (fd: %d): %d - %s", iofd->fd, errno, strerror(errno)); + } + } else { + iofd->writebuf.bufpos -= res; + 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) + { + 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); +#endif + iohandler_try_write(iofd); + } + //close IODescriptor + if(iofd->ssl) + iohandler_ssl_disconnect(iofd); + if(iofd->type == IOTYPE_SERVER || iofd->type == IOTYPE_CLIENT || iofd->type == IOTYPE_STDIN) + close(iofd->fd); + iohandler_remove(iofd, engine_remove); +} + +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); +} + +void iohandler_events(struct IODescriptor *iofd, int readable, int writeable) { + struct IOEvent callback_event; + callback_event.type = IOEVENT_IGNORE; + callback_event.iofd = iofd; + switch(iofd->state) { + case IO_SSLWAIT: + if(!readable && !writeable) { + callback_event.type = IOEVENT_SSLFAILED; + iofd->state = IO_CLOSED; + } else { + iohandler_log(IOLOG_DEBUG, "triggering iohandler_ssl_client_handshake for %s (fd: %d)", iohandler_iotype_name(iofd->type), iofd->fd); + iohandler_ssl_client_handshake(iofd); + } + break; + case IO_CLOSED: + if(iofd->type == IOTYPE_TIMER) + callback_event.type = IOEVENT_TIMEOUT; + break; + case IO_LISTENING: + if(readable) { + callback_event.data.accept_fd = accept(iofd->fd, NULL, 0); + if(callback_event.data.accept_fd < 0) { + iohandler_log(IOLOG_ERROR, "could not accept client (server fd: %d): %d - %s", iofd->fd, errno, strerror(errno)); + } else + callback_event.type = IOEVENT_ACCEPT; + } + break; + case IO_CONNECTING: + if(readable) { //could not connect + callback_event.type = IOEVENT_NOTCONNECTED; + //socklen_t arglen; + //arglen = sizeof(callback_event.data.errid); + //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) { + if(iofd->ssl && !iofd->ssl_active) { + iohandler_log(IOLOG_DEBUG, "triggering iohandler_ssl_connect for %s (fd: %d)", iohandler_iotype_name(iofd->type), iofd->fd); + iohandler_ssl_connect(iofd); + return; + } + callback_event.type = IOEVENT_CONNECTED; + iofd->state = IO_CONNECTED; + engine->update(iofd); + } + break; + case IO_CONNECTED: + if(readable) { + if(iofd->read_lines) { + int bytes; + + if(iofd->ssl_active) + bytes = iohandler_ssl_read(iofd, iofd->readbuf.buffer + iofd->readbuf.bufpos, iofd->readbuf.buflen - iofd->readbuf.bufpos); + else { + if(iofd->type == IOTYPE_STDIN) + #ifdef WIN32 + bytes = readable; + #else + bytes = read(iofd->fd, iofd->readbuf.buffer + iofd->readbuf.bufpos, iofd->readbuf.buflen - iofd->readbuf.bufpos); + #endif + else + bytes = recv(iofd->fd, iofd->readbuf.buffer + iofd->readbuf.bufpos, iofd->readbuf.buflen - iofd->readbuf.bufpos, 0); + } + 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++) { + if(iofd->readbuf.buffer[i] == '\r' && iofd->readbuf.buffer[i+1] == '\n') + iofd->readbuf.buffer[i] = 0; + 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 >= IO_LINE_LEN) { //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; + } + } + used_bytes = i+1; + iohandler_trigger_event(&callback_event); + } + } + if(used_bytes) { + if(used_bytes == iofd->readbuf.bufpos) { + iofd->readbuf.bufpos = 0; + 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; + } + } + callback_event.type = IOEVENT_IGNORE; + } + } else + callback_event.type = IOEVENT_READABLE; + } + if(writeable) { + iohandler_try_write(iofd); + } + break; + } + if(callback_event.type == IOEVENT_IGNORE && !readable && !writeable) + callback_event.type = IOEVENT_TIMEOUT; + if(callback_event.type != IOEVENT_IGNORE) + iohandler_trigger_event(&callback_event); +} + +void iohandler_poll() { + if(engine) { + IOSYNCHRONIZE(io_poll_sync); //quite senceless multithread support... better support will follow + struct timeval timeout; + timeout.tv_sec = IO_MAX_TIMEOUT; + timeout.tv_usec = 0; + engine->loop(&timeout); + IODESYNCHRONIZE(io_poll_sync); + } +} + +//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"; + case IO_SSLWAIT: + return "IO_SSLWAIT"; + 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 new file mode 100644 index 0000000..208b854 --- /dev/null +++ b/src/IOHandler.h @@ -0,0 +1,121 @@ +/* IOHandler.h - IOMultiplexer + * Copyright (C) 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 . + */ +#ifndef _IOHandler_h +#define _IOHandler_h +#include +#include /* struct timeval */ + +#define IO_READ_BUFLEN 1024 +#define IO_MAX_TIMEOUT 10 +#define IO_LINE_LEN 1024 + +struct timeval; +struct IODescriptor; +struct IOEvent; +struct IOSSLNode; + +enum IOLogType { + IOLOG_DEBUG, + IOLOG_WARNING, + IOLOG_ERROR, + IOLOG_FATAL +}; + +#define IOHANDLER_CALLBACK(NAME) void NAME(struct IOEvent *event) +typedef IOHANDLER_CALLBACK(iohandler_callback); + +#define IOHANDLER_LOG_BACKEND(NAME) void NAME(enum IOLogType type, const char *line) +typedef IOHANDLER_LOG_BACKEND(iohandler_log_callback); + +extern iohandler_log_callback *iolog_backend; + +enum IOType { + IOTYPE_UNKNOWN, /* ignore descriptor (uninitialized) */ + IOTYPE_SERVER, /* server socket */ + IOTYPE_CLIENT, /* client socket */ + IOTYPE_STDIN, /* stdin */ + IOTYPE_TIMER /* timer */ +}; + +enum IOStatus { + IO_CLOSED, /* descriptor is dead (socket waiting for removal or timer) */ + IO_LISTENING, /* descriptor is waiting for connections (server socket) */ + IO_CONNECTING, /* descriptor is waiting for connection approval (connecting client socket) */ + IO_CONNECTED, /* descriptor is connected (connected client socket) */ + IO_SSLWAIT /* waiting for SSL backend (e.g. handshake) */ +}; + +enum IOEventType { + IOEVENT_IGNORE, + IOEVENT_READABLE, /* socket is readable - not read anything yet, could also be disconnect notification */ + IOEVENT_RECV, /* client socket received something (recv_str valid) */ + IOEVENT_CONNECTED, /* client socket connected successful */ + IOEVENT_NOTCONNECTED, /* client socket could not connect (errid valid) */ + IOEVENT_CLOSED, /* client socket lost connection (errid valid) */ + IOEVENT_ACCEPT, /* server socket accepted new connection (accept_fd valid) */ + IOEVENT_TIMEOUT, /* timer timed out */ + IOEVENT_SSLFAILED /* failed to initialize SSL session */ +}; + +struct IOBuffer { + char *buffer; + size_t bufpos, buflen; +}; + +struct IODescriptor { + int fd; + enum IOType type; + enum IOStatus state; + struct timeval timeout; + iohandler_callback *callback; + struct IOBuffer readbuf; + struct IOBuffer writebuf; + void *data; + int read_lines : 1; + int ssl : 1; + int ssl_active : 1; + int ssl_hs_read : 1; + int ssl_hs_write : 1; + struct IOSSLNode *sslnode; + + struct IODescriptor *next, *prev; +}; + +struct IOEvent { + enum IOEventType type; + struct IODescriptor *iofd; + union { + char *recv_str; + int accept_fd; + int errid; + } data; +}; + +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, iohandler_callback *callback); +void iohandler_write(struct IODescriptor *iofd, const char *line); +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(); + +#endif diff --git a/src/IOHandler_SSL.c b/src/IOHandler_SSL.c new file mode 100644 index 0000000..d9e1d19 --- /dev/null +++ b/src/IOHandler_SSL.c @@ -0,0 +1,162 @@ +/* IOHandler_SSL.c - IOMultiplexer + * Copyright (C) 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_SSL.h" + +void iohandler_ssl_init() { +#ifdef HAVE_SSL + SSL_library_init(); + SSL_load_error_strings(); +#endif +} + +void iohandler_ssl_connect(struct IODescriptor *iofd) { +#ifdef HAVE_SSL + iofd->state = IO_SSLWAIT; + struct IOSSLNode *sslnode = malloc(sizeof(*sslnode)); + sslnode->sslContext = SSL_CTX_new(SSLv23_client_method()); + if(!sslnode->sslContext) + goto ssl_connect_err; + sslnode->sslHandle = SSL_new(sslnode->sslContext); + if(!sslnode->sslHandle) + goto ssl_connect_err; + if(!SSL_set_fd(sslnode->sslHandle, iofd->fd)) + goto ssl_connect_err; + SSL_set_connect_state(sslnode->sslHandle); + iofd->sslnode = sslnode; + iohandler_ssl_client_handshake(iofd); + return; +ssl_connect_err: + free(sslnode); + iohandler_events(iofd, 0, 0); +#endif +} + +void iohandler_ssl_client_handshake(struct IODescriptor *iofd) { +#ifdef HAVE_SSL + // Perform an SSL handshake. + int ret = SSL_do_handshake(iofd->sslnode->sslHandle); + iofd->ssl_hs_read = 0; + iofd->ssl_hs_write = 0; + switch(SSL_get_error(iofd->sslnode->sslHandle, ret)) { + case SSL_ERROR_NONE: + iofd->state = IO_CONNECTING; + iofd->ssl_active = 1; + iohandler_log(IOLOG_DEBUG, "SSL handshake for %s (fd: %d) successful", iohandler_iotype_name(iofd->type), iofd->fd); + iohandler_events(iofd, 0, 1); //perform IOEVENT_CONNECTED event + break; + case SSL_ERROR_WANT_READ: + iofd->ssl_hs_read = 1; + iohandler_log(IOLOG_DEBUG, "SSL_do_handshake for %s (fd: %d) returned SSL_ERROR_WANT_READ", iohandler_iotype_name(iofd->type), iofd->fd); + break; + case SSL_ERROR_WANT_WRITE: + iofd->ssl_hs_write = 1; + iohandler_log(IOLOG_DEBUG, "SSL_do_handshake for %s (fd: %d) returned SSL_ERROR_WANT_WRITE", iohandler_iotype_name(iofd->type), iofd->fd); + break; + default: + iohandler_log(IOLOG_ERROR, "SSL_do_handshake for %s (fd: %d) failed with ", iohandler_iotype_name(iofd->type), iofd->fd); + iohandler_events(iofd, 0, 0); + break; + } +#endif +} + +void iohandler_ssl_disconnect(struct IODescriptor *iofd) { +#ifdef HAVE_SSL + if(!iofd->sslnode) return; + SSL_shutdown(iofd->sslnode->sslHandle); + SSL_free(iofd->sslnode->sslHandle); + SSL_CTX_free(iofd->sslnode->sslContext); + free(iofd->sslnode); + iofd->sslnode = NULL; + iofd->ssl_active = 0; +#endif +} + +int iohandler_ssl_read(struct IODescriptor *iofd, char *buffer, int len) { +#ifdef HAVE_SSL + if(!iofd->sslnode) return 0; + int ret = SSL_read(iofd->sslnode->sslHandle, buffer, len); + int update = (iofd->ssl_hs_read || iofd->ssl_hs_write); + iofd->ssl_hs_read = 0; + iofd->ssl_hs_write = 0; + switch(SSL_get_error(iofd->sslnode->sslHandle, ret)) { + case SSL_ERROR_NONE: + case SSL_ERROR_ZERO_RETURN: + if(update) + iohandler_update(iofd); + return ret; + break; + case SSL_ERROR_WANT_READ: + iofd->ssl_hs_read = 1; + iohandler_update(iofd); + iohandler_log(IOLOG_DEBUG, "SSL_read for %s (fd: %d) returned SSL_ERROR_WANT_READ", iohandler_iotype_name(iofd->type), iofd->fd); + errno = EAGAIN; + return -1; + break; + case SSL_ERROR_WANT_WRITE: + iofd->ssl_hs_write = 1; + iohandler_update(iofd); + iohandler_log(IOLOG_DEBUG, "SSL_read for %s (fd: %d) returned SSL_ERROR_WANT_WRITE", iohandler_iotype_name(iofd->type), iofd->fd); + errno = EAGAIN; + return -1; + break; + default: + iohandler_log(IOLOG_ERROR, "SSL_read for %s (fd: %d) failed with ", iohandler_iotype_name(iofd->type), iofd->fd); + return -1; + break; + } +#endif + return 0; +} + +int iohandler_ssl_write(struct IODescriptor *iofd, char *buffer, int len) { +#ifdef HAVE_SSL + if(!iofd->sslnode) return 0; + int ret = SSL_write(iofd->sslnode->sslHandle, buffer, len); + int update = (iofd->ssl_hs_read || iofd->ssl_hs_write); + iofd->ssl_hs_read = 0; + iofd->ssl_hs_write = 0; + switch(SSL_get_error(iofd->sslnode->sslHandle, ret)) { + case SSL_ERROR_NONE: + case SSL_ERROR_ZERO_RETURN: + if(update) + iohandler_update(iofd); + return ret; + break; + case SSL_ERROR_WANT_READ: + iofd->ssl_hs_read = 1; + iohandler_update(iofd); + iohandler_log(IOLOG_DEBUG, "SSL_write for %s (fd: %d) returned SSL_ERROR_WANT_READ", iohandler_iotype_name(iofd->type), iofd->fd); + errno = EAGAIN; + return -1; + break; + case SSL_ERROR_WANT_WRITE: + iofd->ssl_hs_write = 1; + iohandler_update(iofd); + iohandler_log(IOLOG_DEBUG, "SSL_write for %s (fd: %d) returned SSL_ERROR_WANT_WRITE", iohandler_iotype_name(iofd->type), iofd->fd); + errno = EAGAIN; + return -1; + break; + default: + iohandler_log(IOLOG_ERROR, "SSL_write for %s (fd: %d) failed with ", iohandler_iotype_name(iofd->type), iofd->fd); + return -1; + break; + } +#endif + return 0; +} diff --git a/src/IOHandler_SSL.h b/src/IOHandler_SSL.h new file mode 100644 index 0000000..b1ccf0b --- /dev/null +++ b/src/IOHandler_SSL.h @@ -0,0 +1,44 @@ +/* IOHandler_SSL.h - IOMultiplexer + * Copyright (C) 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 . + */ +#ifndef _IOHandler_SSL_h +#define _IOHandler_SSL_h + +struct IODescriptor; + +#ifdef HAVE_SSL +#include +#include +#include + +struct IOSSLNode { + SSL *sslHandle; + SSL_CTX *sslContext; +}; +#else +struct IOSSLNode { + //just unused +}; +#endif + +void iohandler_ssl_init(); +void iohandler_ssl_connect(struct IODescriptor *iofd); +void iohandler_ssl_client_handshake(struct IODescriptor *iofd); +void iohandler_ssl_disconnect(struct IODescriptor *iofd); +int iohandler_ssl_read(struct IODescriptor *iofd, char *buffer, int len); +int iohandler_ssl_write(struct IODescriptor *iofd, char *buffer, int len); + +#endif diff --git a/src/IRCParser.c b/src/IRCParser.c index 60b48d9..f9e79e0 100644 --- a/src/IRCParser.c +++ b/src/IRCParser.c @@ -35,27 +35,10 @@ int statistics_privmsg = 0; int statistics_network_users = 0; int statistics_network_channels = 0; -static void parse_line(struct ClientSocket *client, char *line); static void register_irc_function(char *command, irc_cmd_t *func); static void parse_raw(struct ClientSocket *client, char *from, char *cmd, char **argv, int argc); -int parse_lines(struct ClientSocket *client, char *lines, int len) { - int i, used = 0; - char *line = lines; - for(i = 0; i < len; i++) { - if(lines[i] == '\r') //just zero it out :D - lines[i] = 0; - if(lines[i] == '\n') { - lines[i] = 0; - parse_line(client, line); - line = lines+(i+1); - used = i+1; - } - } - return used; -} - -static void parse_line(struct ClientSocket *client, char *line) { +void parse_line(struct ClientSocket *client, char *line) { int argc = 0; char *argv[MAXNUMPARAMS]; #ifdef HAVE_THREADS diff --git a/src/IRCParser.h b/src/IRCParser.h index 7893bc6..e23a1ec 100644 --- a/src/IRCParser.h +++ b/src/IRCParser.h @@ -36,7 +36,7 @@ extern int statistics_privmsg; extern int statistics_network_users; extern int statistics_network_channels; -int parse_lines(struct ClientSocket *client, char *lines, int len); +void parse_line(struct ClientSocket *client, char *line); void bot_disconnect(struct ClientSocket *client); void init_parser(); void free_parser(); diff --git a/src/IRCQueue.c b/src/IRCQueue.c index be472b3..d68f098 100644 --- a/src/IRCQueue.c +++ b/src/IRCQueue.c @@ -16,6 +16,7 @@ */ #include "IRCQueue.h" #include "ClientSocket.h" +#include "IOHandler.h" #define MAXPENALTY 8 /* 4 messages */ @@ -26,12 +27,16 @@ struct QueueEntry { struct BotQueue { struct ClientSocket *client; - int penalty; + struct IODescriptor *iofd; + int penalty : 8; + int rem_penalty : 8; struct QueueEntry *fastqueue_first, *fastqueue_last; struct QueueEntry *normalqueue_first, *normalqueue_last; struct QueueEntry *textqueue_first, *textqueue_last; }; +static IOHANDLER_CALLBACK(queue_callback); + static struct BotQueue *initialize_queue(struct ClientSocket *client) { struct BotQueue *queue = malloc(sizeof(*queue)); if (!queue) { @@ -50,12 +55,19 @@ static struct BotQueue *initialize_queue(struct ClientSocket *client) { return queue; } +static int calculate_penalty(char *message) { + int msglen = strlen(message); + int penalty = (2 + msglen / 100); + return penalty; +} + int queue_add(struct ClientSocket *client, char* msg, int len) { if(!client->queue) client->queue = initialize_queue(client); struct BotQueue *queue = client->queue; char *args = strstr(msg, " "); int type; + int add_queue = 0; if(args) { *args = '\0'; if(!stricmp(msg, "MODE")) @@ -75,47 +87,78 @@ int queue_add(struct ClientSocket *client, char* msg, int len) { *args = ' '; } else type = 2; - struct QueueEntry *entry = malloc(sizeof(*entry)); - if (!entry) { - perror("malloc() failed"); - return 0; + + //check if we need to queue + switch(type) { + case 1: + if(queue->textqueue_first) { + add_queue = 1; + break; + } + case 2: + if(queue->normalqueue_first) { + add_queue = 1; + break; + } + case 3: + if(queue->fastqueue_first) { + add_queue = 1; + break; + } + default: + if(queue->penalty >= MAXPENALTY) + add_queue = 1; + break; } - entry->msg = strdup(msg); - entry->next = NULL; - if(type == 1) { //low priority - if(queue->textqueue_last) { - queue->textqueue_last->next = entry; - queue->textqueue_last = entry; - } else { - queue->textqueue_last = entry; - queue->textqueue_first = entry; + + if(!add_queue) { + int penalty = calculate_penalty(msg); + write_socket_force(client, msg, len); + queue->penalty += penalty; + if(!queue->iofd) { + struct timeval timeout; + gettimeofday(&timeout, NULL); + queue->rem_penalty = (MAXPENALTY - queue->penalty) + 1; + timeout.tv_sec += queue->rem_penalty; + queue->iofd = iohandler_timer(timeout, queue_callback); } - } else if(type == 2) { //normal priority - if(queue->normalqueue_last) { - queue->normalqueue_last->next = entry; - queue->normalqueue_last = entry; - } else { - queue->normalqueue_last = entry; - queue->normalqueue_first = entry; + } else { + struct QueueEntry *entry = malloc(sizeof(*entry)); + if (!entry) { + perror("malloc() failed"); + return 0; } - } else if(type == 3) { //high priority - if(queue->fastqueue_last) { - queue->fastqueue_last->next = entry; - queue->fastqueue_last = entry; - } else { - queue->fastqueue_last = entry; - queue->fastqueue_first = entry; + entry->msg = strdup(msg); + entry->next = NULL; + if(type == 1) { //low priority + if(queue->textqueue_last) { + queue->textqueue_last->next = entry; + queue->textqueue_last = entry; + } else { + queue->textqueue_last = entry; + queue->textqueue_first = entry; + } + } else if(type == 2) { //normal priority + if(queue->normalqueue_last) { + queue->normalqueue_last->next = entry; + queue->normalqueue_last = entry; + } else { + queue->normalqueue_last = entry; + queue->normalqueue_first = entry; + } + } else if(type == 3) { //high priority + if(queue->fastqueue_last) { + queue->fastqueue_last->next = entry; + queue->fastqueue_last = entry; + } else { + queue->fastqueue_last = entry; + queue->fastqueue_first = entry; + } } } return 1; } -static int calculate_penalty(char *message) { - int msglen = strlen(message); - int penalty = (2 + msglen / 100); - return penalty; -} - static void dequeue_bot(struct ClientSocket *client) { if(client->queue->penalty >= MAXPENALTY) return; int penalty; @@ -183,37 +226,30 @@ void queue_destroy(struct ClientSocket *client) { free(entry->msg); free(entry); } + if(client->queue->iofd) + iohandler_close(client->queue->iofd); free(client->queue); client->queue = NULL; } -static struct timeval lastloop; -void queue_init() { - gettimeofday(&lastloop, NULL); -} - - -void queue_loop() { - struct ClientSocket *bot; - struct timeval now; - gettimeofday(&now, NULL); - long mtime, seconds, useconds; - seconds = now.tv_sec - lastloop.tv_sec; - useconds = now.tv_usec - lastloop.tv_usec; - mtime = ((seconds) * 1000 + useconds/1000.0) + 0.5; - int fullseconds = mtime/1000; - if(fullseconds) { - lastloop.tv_sec += fullseconds; - for(bot = getBots(0, NULL); bot; bot = getBots(0, bot)) { - if(bot->queue && bot->queue->penalty) { - bot->queue->penalty -= fullseconds; - if(bot->queue->penalty < 0) - bot->queue->penalty = 0; - } +static IOHANDLER_CALLBACK(queue_callback) { + struct BotQueue *queue = event->iofd->data; + switch(event->type) { + case IOEVENT_TIMEOUT: + queue->penalty -= queue->rem_penalty; + dequeue_bot(queue->client); + if(queue->penalty > 0) { + struct timeval timeout; + gettimeofday(&timeout, NULL); + queue->rem_penalty = (MAXPENALTY - queue->penalty) + 1; + timeout.tv_sec += queue->rem_penalty; + queue->iofd = iohandler_timer(timeout, queue_callback); + } else { + queue->iofd = NULL; + queue->penalty = 0; } - } - for(bot = getBots(0, NULL); bot; bot = getBots(0, bot)) { - if(bot->queue) - dequeue_bot(bot); + break; + default: + break; } } diff --git a/src/IRCQueue.h b/src/IRCQueue.h index 9059631..3f8ae81 100644 --- a/src/IRCQueue.h +++ b/src/IRCQueue.h @@ -23,7 +23,5 @@ struct ClientSocket; int queue_add(struct ClientSocket *client, char* msg, int len); void queue_destroy(struct ClientSocket *client); -void queue_init(); -void queue_loop(); #endif \ No newline at end of file diff --git a/src/QServer.c b/src/QServer.c index 2d77a6b..93d2a4f 100644 --- a/src/QServer.c +++ b/src/QServer.c @@ -24,6 +24,7 @@ #include "WHOHandler.h" #include "ConfigParser.h" #include "bots.h" +#include "IOHandler.h" #ifdef WIN32 typedef uint32_t socklen_t; @@ -32,61 +33,61 @@ typedef uint32_t socklen_t; #define QSERVER_TIMEOUT 30 #define QSERVER_MAXCLIENTS 100 -#define QSERVER_FLAG_DISCONNECT 0x01 -#define QSERVER_FLAG_AUTHED 0x02 -#define QSERVER_FLAG_IN_USE 0x04 +#define QSERVER_FLAG_AUTHED 0x01 +#define QSERVER_FLAG_IN_USE 0x02 struct QServerClient { - int sock; + struct IODescriptor *iofd; unsigned int flags; - time_t lastmsg; - char buffer[MAXLEN]; - int bufferpos; int references; struct QServerClient *next; }; -static int server_sockfd = 0; +static struct IODescriptor *server_iofd = NULL; struct QServerClient *qserver_clients = NULL; static int qserver_clientcount = 0; +static IOHANDLER_CALLBACK(qserver_callback); + void qserver_init() { if(get_int_field("QServer.enabled")) { - server_sockfd = socket(AF_INET, SOCK_STREAM, 0); - if (server_sockfd < 0) - return; - struct sockaddr_in serv_addr; - memset(&serv_addr, 0, sizeof(serv_addr)); - int portno = get_int_field("QServer.port"); - if(!portno) - portno = 7499; - serv_addr.sin_family = AF_INET; - serv_addr.sin_addr.s_addr = INADDR_ANY; - serv_addr.sin_port = htons(portno); - if (bind(server_sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) - return; - listen(server_sockfd,5); + char *host = get_string_field("QServer.host"); + if(!host) + host = "0.0.0.0"; + int port = get_int_field("QServer.port"); + if(!port) + port = 7499; + server_iofd = iohandler_listen(host, port, qserver_callback); } } +void qserver_free() { + struct QServerClient *client, *next; + for (client = qserver_clients; client; client = next) { + next = client->next; + if(client->iofd) + iohandler_close(client->iofd); + free(client); + } + qserver_clients = NULL; + qserver_clientcount = 0; + iohandler_close(server_iofd); + server_iofd = NULL; +} + static int qserver_write(struct QServerClient *client, char* msg, int len) { - if (!(client && !(client->flags & QSERVER_FLAG_DISCONNECT))) return 0; + if (!client || !client->iofd) return 0; if(!len) len = strlen(msg); - int ret = 1; - #ifdef WIN32 - ret = send(client->sock, msg, len, 0); - #else - ret = write(client->sock, msg, len); - #endif - return ret; + iohandler_send(client->iofd, msg, len); + return 1; } static void qserver_put(struct QServerClient *client, const char *text, ...) { va_list arg_list; char sendBuf[MAXLEN]; int pos; - if (!(client && !(client->flags & QSERVER_FLAG_DISCONNECT))) return; + if (!client || !client->iofd) return; sendBuf[0] = '\0'; va_start(arg_list, text); pos = vsnprintf(sendBuf, MAXLEN - 2, text, arg_list); @@ -97,6 +98,13 @@ static void qserver_put(struct QServerClient *client, const char *text, ...) { qserver_write(client, sendBuf, pos+1); } +static void qserver_update_lastmsg(struct QServerClient *client) { + struct timeval timeout; + gettimeofday(&timeout, NULL); + timeout.tv_sec += QSERVER_TIMEOUT; + iohandler_set_timeout(client->iofd, &timeout); +} + static void qserver_parse_A(struct QServerClient *client, char **argv, int argc) { if(client->flags & QSERVER_FLAG_AUTHED) { qserver_put(client, "E :Already Authed"); @@ -111,7 +119,7 @@ static void qserver_parse_A(struct QServerClient *client, char **argv, int argc) return; } client->flags |= QSERVER_FLAG_AUTHED; - client->lastmsg = time(0); + qserver_update_lastmsg(client); qserver_put(client, "A :Logged in"); } @@ -120,7 +128,7 @@ static void qserver_parse_A(struct QServerClient *client, char **argv, int argc) qserver_put(client, "E :Not Authed");\ return;\ }\ - client->lastmsg = time(0);\ + qserver_update_lastmsg(client);\ } static void qserver_parse_U(struct QServerClient *client, char **argv, int argc); @@ -129,7 +137,7 @@ static void qserver_parse_AC(struct QServerClient *client, char **argv, int argc static void qserver_parse_ACU(struct QServerClient *client, char **argv, int argc); static void qserver_parse_R(struct QServerClient *client, char **argv, int argc); -static void qserver_parse(struct QServerClient *client, char *line, int len) { +static void qserver_parse(struct QServerClient *client, char *line) { int argc = 0; char *argv[MAXNUMPARAMS]; while(*line) { @@ -163,115 +171,59 @@ static void qserver_parse(struct QServerClient *client, char *line, int len) { qserver_put(client, "E :Unknown Command"); } -void qserver_loop() { - if(!get_int_field("QServer.enabled")) +static void qserver_accept(int sockfd) { + struct IODescriptor *client_iofd = iohandler_add(sockfd, IOTYPE_CLIENT, NULL, qserver_callback); + client_iofd->state = IO_CONNECTED; + iohandler_update(client_iofd); + if(qserver_clientcount >= QSERVER_MAXCLIENTS) { + iohandler_printf(client_iofd, "E :Maximum QServer Connections reached"); + iohandler_close(client_iofd); return; - struct timeval tv; + } + struct QServerClient *client = malloc(sizeof(*client)); + client->iofd = client_iofd; + client->references = 0; + client->next = qserver_clients; + qserver_clients = client; + qserver_clientcount++; +} + +static void qserver_cleanup() { struct QServerClient *client, *next, *prev = NULL; - int ret; - time_t now = time(0); - fd_set fds; - tv.tv_sec = 0; - tv.tv_usec = 0; - FD_ZERO(&fds); - ret = server_sockfd; - FD_SET(server_sockfd, &fds); for (client = qserver_clients; client; client = next) { next = client->next; - if((client->flags & (QSERVER_FLAG_DISCONNECT | QSERVER_FLAG_IN_USE)) == QSERVER_FLAG_DISCONNECT) { - close(client->sock); + if(client->iofd == NULL && !(client->flags & QSERVER_FLAG_IN_USE)) { if(prev) prev->next = client->next; else qserver_clients = client->next; qserver_clientcount--; free(client); - continue; - } - prev = client; - if(client->flags & QSERVER_FLAG_DISCONNECT) continue; - if(now - client->lastmsg > QSERVER_TIMEOUT) { - qserver_put(client, "E :Timeout"); - client->flags |= QSERVER_FLAG_DISCONNECT; - continue; - } - FD_SET(client->sock, &fds); - if(client->sock > ret) - ret = client->sock; - } - ret = select(ret + 1, &fds, NULL, NULL, &tv); - if(ret == 0) { - return; - } - if(FD_ISSET(server_sockfd, &fds)) { - //new connection - struct sockaddr_in cli_addr; - #ifdef WIN32 - int clilen; - #else - socklen_t clilen; - #endif - client = malloc(sizeof(*client)); - clilen = sizeof(cli_addr); - client->sock = accept(server_sockfd, (struct sockaddr *) &cli_addr, &clilen); - client->flags = 0; - if(qserver_clientcount >= QSERVER_MAXCLIENTS) { - qserver_put(client, "E :Maximum QServer Connections reached"); - close(client->sock); - free(client); - } else { - client->lastmsg = now; - client->bufferpos = 0; - client->references = 0; - client->next = qserver_clients; - qserver_clients = client; - qserver_clientcount++; - } - } - int bytes, i; - char buffer[MAXLEN]; - for (client = qserver_clients; client; client = next) { - next = client->next; - if(FD_ISSET(client->sock, &fds)) { - #ifdef WIN32 - bytes = recv(client->sock, buffer, sizeof(buffer), 0); - #else - bytes = read(client->sock, buffer, sizeof(buffer)); - #endif - if(bytes <= 0) { - client->flags |= QSERVER_FLAG_DISCONNECT; - continue; - } - for(i = 0; i < bytes; i++) { - if(client->bufferpos == MAXLEN-1) { - //buffer overflow - qserver_put(client, "E :Buffer Overflow"); - client->flags |= QSERVER_FLAG_DISCONNECT; - break; - } - if(buffer[i] == '\r') continue; - else if(buffer[i] == '\n') { - client->buffer[client->bufferpos] = '\0'; - qserver_parse(client, client->buffer, client->bufferpos); - client->bufferpos = 0; - } else { - client->buffer[client->bufferpos++] = buffer[i]; - } - } } } } -void qserver_free() { - struct QServerClient *client, *next; - for (client = qserver_clients; client; client = next) { - next = client->next; - close(client->sock); - free(client); +static IOHANDLER_CALLBACK(qserver_callback) { + struct QServerClient *client = event->iofd->data; + switch(event->type) { + case IOEVENT_TIMEOUT: + qserver_put(client, "E :Timeout"); + client->iofd = NULL; + break; + case IOEVENT_RECV: + qserver_parse(client, event->data.recv_str); + break; + case IOEVENT_CLOSED: + iohandler_close(client->iofd); + client->iofd = NULL; + break; + case IOEVENT_ACCEPT: + qserver_accept(event->data.accept_fd); + break; + default: + break; } - qserver_clients = NULL; - qserver_clientcount = 0; - close(server_sockfd); + qserver_cleanup(); } /* diff --git a/src/QServer.h b/src/QServer.h index 4bda03e..59bbb5d 100644 --- a/src/QServer.h +++ b/src/QServer.h @@ -20,7 +20,6 @@ #include "main.h" void qserver_init(); -void qserver_loop(); void qserver_free(); #endif \ No newline at end of file diff --git a/src/main.c b/src/main.c index 2e0e5f3..d585ada 100644 --- a/src/main.c +++ b/src/main.c @@ -35,12 +35,12 @@ #include "IRCQueue.h" #include "DBHelper.h" #include "ConfigParser.h" -#include "ssl.h" #include "QServer.h" #include "version.h" #include "modules.h" #include "module_commands.h" #include "ModuleFunctions.h" +#include "IOHandler.h" time_t start_time; static int running, hard_restart; @@ -108,30 +108,23 @@ static int load_mysql_config() { return 1; } -#ifdef HAVE_THREADS -pthread_t *current_threads = NULL; +static TIMEQ_CALLBACK(clear_cache) { + timeq_add(CLEAR_CACHE_INTERVAL, 0, clear_cache, NULL); + clearTempUsers(); + destroyEvents(); + mysql_free(); +} -void * thread_main(void *arg) { - time_t socket_wait; +void *thread_main(void *arg) { while(running) { - socket_wait = time(0) + SOCKET_SELECT_TIME; - do { - if(!socket_loop(SOCKET_SELECT_TIME)) { - if(!running) break; - putlog(LOGLEVEL_ERROR, "No more active Bots... shutting down.\n"); - cleanup(); - exit(0); - } - } while(time(0) < socket_wait); - if(!running) break; - clearTempUsers(); - destroyEvents(); - mysql_free(); + iohandler_poll(); } - running_threads--; return NULL; } +#ifdef HAVE_THREADS +pthread_t *current_threads = NULL; + int getCurrentThreadID() { if(!current_threads) return 0; int i; @@ -262,18 +255,6 @@ main: start_time = time(0); - #ifdef WIN32 - int res; - WSADATA wsadata; - // Start Windows Sockets. - res = WSAStartup(MAKEWORD(2, 0), &wsadata); - if (res) - { - perror("Unable to start Windows Sockets"); - return 0; - } - #endif - statistics_enabled = get_int_field("statistics.enable"); #ifdef HAVE_THREADS @@ -282,11 +263,7 @@ main: THREAD_MUTEX_INIT(whohandler_mass_sync); #endif - queue_init(); - init_sockets(); - init_timeq(); init_lang(); - ssl_init(); init_parser(); init_UserNode(); init_ChanNode(); @@ -309,6 +286,8 @@ main: timeq_add(90, 0, main_checkauths, NULL); + timeq_add(CLEAR_CACHE_INTERVAL, 0, clear_cache, NULL); + int worker_threads = get_int_field("General.worker_threads"); if(!worker_threads) worker_threads = 1; running = 1; @@ -319,38 +298,13 @@ main: running_threads++; pthread_create(¤t_threads[tid_id], NULL, thread_main, NULL); } - int usleep_delay = 1000000 / TICKS_PER_SECOND; - while(running) { - timeq_tick(); - loop_modules(); - qserver_loop(); - queue_loop(); - mysql_free(); - usleep(usleep_delay); - } + #endif + thread_main(NULL); + #ifdef HAVE_THREADS for(tid_id = 0; tid_id < worker_threads; tid_id++) { pthread_join(current_threads[tid_id], NULL); } running_threads = 0; - #else - time_t socket_wait; - while(running) { - socket_wait = time(0) + SOCKET_SELECT_TIME; - do { - if(!socket_loop(SOCKET_SELECT_TIME)) { - putlog(LOGLEVEL_ERROR, "No more active Bots... shutting down.\n"); - cleanup(); - exit(0); - } - } while(time(0) < socket_wait); - timeq_tick(); - loop_modules(); - clearTempUsers(); - destroyEvents(); - qserver_loop(); - queue_loop(); - mysql_free(); - } #endif cleanup(); if(hard_restart) { diff --git a/src/modules.c b/src/modules.c index ba3562a..eda8f46 100644 --- a/src/modules.c +++ b/src/modules.c @@ -89,7 +89,7 @@ void *global_functions[] = { /* 030 */ (Function) create_socket, /* 031 */ (Function) connect_socket, /* 032 */ (Function) close_socket, -/* 033 */ (Function) disconnect_socket, +/* 033 */ (Function) destroy_socket, /* 034 */ (Function) write_socket, /* 035 */ (Function) putsock, /* 036 */ (Function) getBots, @@ -308,7 +308,6 @@ struct ModuleInfo *loadModule(char *name) { } void* initfunc = dlsym(module, "init_module"); void* startfunc = dlsym(module, "start_module"); - void* loopfunc = dlsym(module, "loop_module"); void* stopfunc = dlsym(module, "stop_module"); void* modversion = dlsym(module, "modversion"); #else @@ -320,11 +319,10 @@ struct ModuleInfo *loadModule(char *name) { } FARPROC initfunc = GetProcAddress(module, "init_module"); FARPROC startfunc = GetProcAddress(module, "start_module"); - FARPROC loopfunc = GetProcAddress(module, "loop_module"); FARPROC stopfunc = GetProcAddress(module, "stop_module"); FARPROC modversion = GetProcAddress(module, "modversion"); #endif - if(!startfunc || !loopfunc || !stopfunc || !modversion) { + if(!startfunc || !stopfunc || !modversion) { putlog(LOGLEVEL_ERROR, "Error loading module '%s': required symbols not found.\n", name); return NULL; } @@ -349,7 +347,6 @@ struct ModuleInfo *loadModule(char *name) { modinfo->module_id = module_id; modinfo->module = module; modinfo->startfunc = startfunc; - modinfo->loopfunc = loopfunc; modinfo->stopfunc = stopfunc; modinfo->state = 0; modinfo->next = modules; @@ -451,13 +448,6 @@ void start_modules() { } } -void loop_modules() { - struct ModuleInfo *modinfo; - for(modinfo = modules; modinfo; modinfo = modinfo->next) { - ((void (*)(void)) modinfo->loopfunc)(); - } -} - void stop_modules() { struct ModuleInfo *modinfo, *next; for(modinfo = modules; modinfo; modinfo = next) { diff --git a/src/modules.h b/src/modules.h index 5468297..da2c6e0 100644 --- a/src/modules.h +++ b/src/modules.h @@ -31,7 +31,6 @@ struct ModuleInfo { #endif int state; void *startfunc; - void *loopfunc; void *stopfunc; struct ModuleInfo *next; }; @@ -40,7 +39,6 @@ struct ModuleInfo { void loadModules(); struct ModuleInfo *loadModule(char *name); void start_modules(); -void loop_modules(); void stop_modules(); int module_loaded(int module_id); diff --git a/src/modules/DummyServ.mod/bot_DummyServ.c b/src/modules/DummyServ.mod/bot_DummyServ.c index 3d6ac1e..ade73ac 100644 --- a/src/modules/DummyServ.mod/bot_DummyServ.c +++ b/src/modules/DummyServ.mod/bot_DummyServ.c @@ -151,10 +151,6 @@ void init_DummyServ(int type) { set_trigger_callback(BOTID, module_id, dummyserv_trigger_callback); } -void loop_DummyServ() { - -} - void free_DummyServ(int type) { unbind_allcmd(BOTID); if(type == MODSTATE_STARTSTOP) { diff --git a/src/modules/DummyServ.mod/bot_DummyServ.h b/src/modules/DummyServ.mod/bot_DummyServ.h index e20bc01..dc5860f 100644 --- a/src/modules/DummyServ.mod/bot_DummyServ.h +++ b/src/modules/DummyServ.mod/bot_DummyServ.h @@ -20,7 +20,6 @@ #include "../../main.h" void init_DummyServ(int type); -void loop_DummyServ(); void free_DummyServ(int type); #endif \ No newline at end of file diff --git a/src/modules/DummyServ.mod/module.c b/src/modules/DummyServ.mod/module.c index b826999..61d9773 100644 --- a/src/modules/DummyServ.mod/module.c +++ b/src/modules/DummyServ.mod/module.c @@ -25,12 +25,8 @@ static void module_start(int type) { init_DummyServ(type); } -static void module_loop() { - loop_DummyServ(); -} - static void module_stop(int type) { free_DummyServ(type); } -MODULE_HEADER(module_initialize, module_start, module_loop, module_stop); +MODULE_HEADER(module_initialize, module_start, module_stop); diff --git a/src/modules/NeonBackup.mod/bot_NeonBackup.c b/src/modules/NeonBackup.mod/bot_NeonBackup.c index b412426..f9d8261 100644 --- a/src/modules/NeonBackup.mod/bot_NeonBackup.c +++ b/src/modules/NeonBackup.mod/bot_NeonBackup.c @@ -202,10 +202,6 @@ void init_NeonBackup(int type) { register_default_language_table(msgtab); } -void loop_NeonBackup() { - -} - void free_NeonBackup(int type) { unbind_allcmd(BOTID); if(type == MODSTATE_STARTSTOP) { diff --git a/src/modules/NeonBackup.mod/bot_NeonBackup.h b/src/modules/NeonBackup.mod/bot_NeonBackup.h index b693d10..8284bed 100644 --- a/src/modules/NeonBackup.mod/bot_NeonBackup.h +++ b/src/modules/NeonBackup.mod/bot_NeonBackup.h @@ -20,7 +20,6 @@ #include "../../main.h" void init_NeonBackup(int type); -void loop_NeonBackup(); void free_NeonBackup(int type); #endif \ No newline at end of file diff --git a/src/modules/NeonBackup.mod/module.c b/src/modules/NeonBackup.mod/module.c index 4b13729..cb5f9a6 100644 --- a/src/modules/NeonBackup.mod/module.c +++ b/src/modules/NeonBackup.mod/module.c @@ -27,12 +27,8 @@ static void module_start(int type) { init_NeonBackup(type); } -static void module_loop() { - loop_NeonBackup(); -} - static void module_stop(int type) { free_NeonBackup(type); } -MODULE_HEADER(module_initialize, module_start, module_loop, module_stop); +MODULE_HEADER(module_initialize, module_start, module_stop); diff --git a/src/modules/NeonFun.mod/bot_NeonFun.c b/src/modules/NeonFun.mod/bot_NeonFun.c index dbef386..c159322 100644 --- a/src/modules/NeonFun.mod/bot_NeonFun.c +++ b/src/modules/NeonFun.mod/bot_NeonFun.c @@ -185,10 +185,6 @@ void init_NeonFun(int type) { set_trigger_callback(BOTID, module_id, neonfun_trigger_callback); } -void loop_NeonFun() { - -} - void free_NeonFun(int type) { unbind_allcmd(BOTID); if(type == MODSTATE_STARTSTOP) { diff --git a/src/modules/NeonFun.mod/bot_NeonFun.h b/src/modules/NeonFun.mod/bot_NeonFun.h index 9b8bdb1..0065182 100644 --- a/src/modules/NeonFun.mod/bot_NeonFun.h +++ b/src/modules/NeonFun.mod/bot_NeonFun.h @@ -20,7 +20,6 @@ #include "../../main.h" void init_NeonFun(int type); -void loop_NeonFun(); void free_NeonFun(int type); struct uno_game; diff --git a/src/modules/NeonFun.mod/module.c b/src/modules/NeonFun.mod/module.c index 9377166..2581834 100644 --- a/src/modules/NeonFun.mod/module.c +++ b/src/modules/NeonFun.mod/module.c @@ -28,12 +28,8 @@ static void module_start(int type) { srand(time(NULL)); } -static void module_loop() { - loop_NeonFun(); -} - static void module_stop(int type) { free_NeonFun(type); } -MODULE_HEADER(module_initialize, module_start, module_loop, module_stop); +MODULE_HEADER(module_initialize, module_start, module_stop); diff --git a/src/modules/NeonHelp.mod/bot_NeonHelp.c b/src/modules/NeonHelp.mod/bot_NeonHelp.c index 410ad1c..e35d85b 100644 --- a/src/modules/NeonHelp.mod/bot_NeonHelp.c +++ b/src/modules/NeonHelp.mod/bot_NeonHelp.c @@ -568,10 +568,6 @@ void init_NeonHelp(int type) { register_default_language_table(msgtab); } -void loop_NeonHelp() { - -} - void free_NeonHelp(int type) { unbind_allcmd(BOTID); if(type == MODSTATE_STARTSTOP) { diff --git a/src/modules/NeonHelp.mod/bot_NeonHelp.h b/src/modules/NeonHelp.mod/bot_NeonHelp.h index 51ca4e2..bc231ec 100644 --- a/src/modules/NeonHelp.mod/bot_NeonHelp.h +++ b/src/modules/NeonHelp.mod/bot_NeonHelp.h @@ -40,7 +40,6 @@ struct NeonHelpNode { }; void init_NeonHelp(int type); -void loop_NeonHelp(); void free_NeonHelp(int type); void neonhelp_destroy_support_request(struct ClientSocket *client, struct NeonHelpNode *helpnode, int do_reply); diff --git a/src/modules/NeonHelp.mod/module.c b/src/modules/NeonHelp.mod/module.c index 400c647..7ae1add 100644 --- a/src/modules/NeonHelp.mod/module.c +++ b/src/modules/NeonHelp.mod/module.c @@ -27,12 +27,8 @@ static void module_start(int type) { init_NeonHelp(type); } -static void module_loop() { - loop_NeonHelp(); -} - static void module_stop(int type) { free_NeonHelp(type); } -MODULE_HEADER(module_initialize, module_start, module_loop, module_stop); +MODULE_HEADER(module_initialize, module_start, module_stop); diff --git a/src/modules/NeonServ.mod/bot_NeonServ.c b/src/modules/NeonServ.mod/bot_NeonServ.c index 477bf4f..4ba6381 100644 --- a/src/modules/NeonServ.mod/bot_NeonServ.c +++ b/src/modules/NeonServ.mod/bot_NeonServ.c @@ -525,10 +525,6 @@ void init_NeonServ(int type) { register_default_language_table(msgtab); } -void loop_NeonServ() { - -} - void free_NeonServ(int type) { unbind_allcmd(BOTID); if(type == MODSTATE_STARTSTOP) { diff --git a/src/modules/NeonServ.mod/bot_NeonServ.h b/src/modules/NeonServ.mod/bot_NeonServ.h index fcd7d18..7d9f714 100644 --- a/src/modules/NeonServ.mod/bot_NeonServ.h +++ b/src/modules/NeonServ.mod/bot_NeonServ.h @@ -22,7 +22,6 @@ struct ChanNode; void init_NeonServ(int type); -void loop_NeonServ(); void free_NeonServ(int type); struct ClientSocket *getBotForChannel(struct ChanNode *chan); diff --git a/src/modules/NeonServ.mod/module.c b/src/modules/NeonServ.mod/module.c index d736384..c169e75 100644 --- a/src/modules/NeonServ.mod/module.c +++ b/src/modules/NeonServ.mod/module.c @@ -27,12 +27,8 @@ static void module_start(int type) { init_NeonServ(type); } -static void module_loop() { - loop_NeonServ(); -} - static void module_stop(int type) { free_NeonServ(type); } -MODULE_HEADER(module_initialize, module_start, module_loop, module_stop); +MODULE_HEADER(module_initialize, module_start, module_stop); diff --git a/src/modules/NeonSpam.mod/bot_NeonSpam.c b/src/modules/NeonSpam.mod/bot_NeonSpam.c index 1fd3a6f..2ef3c74 100644 --- a/src/modules/NeonSpam.mod/bot_NeonSpam.c +++ b/src/modules/NeonSpam.mod/bot_NeonSpam.c @@ -402,10 +402,6 @@ void init_NeonSpam(int type) { register_default_language_table(msgtab); } -void loop_NeonSpam() { - -} - void free_NeonSpam(int type) { unbind_allcmd(BOTID); if(type == MODSTATE_STARTSTOP) { diff --git a/src/modules/NeonSpam.mod/bot_NeonSpam.h b/src/modules/NeonSpam.mod/bot_NeonSpam.h index 072fccc..718b5ff 100644 --- a/src/modules/NeonSpam.mod/bot_NeonSpam.h +++ b/src/modules/NeonSpam.mod/bot_NeonSpam.h @@ -161,7 +161,6 @@ struct NeonSpamJoinNode { }; void init_NeonSpam(int type); -void loop_NeonSpam(); void free_NeonSpam(int type); void freeNeonSpamSettings(struct NeonSpamSettings *settings); diff --git a/src/modules/NeonSpam.mod/module.c b/src/modules/NeonSpam.mod/module.c index 2758e89..20f99ac 100644 --- a/src/modules/NeonSpam.mod/module.c +++ b/src/modules/NeonSpam.mod/module.c @@ -27,12 +27,8 @@ static void module_start(int type) { init_NeonSpam(type); } -static void module_loop() { - loop_NeonSpam(); -} - static void module_stop(int type) { free_NeonSpam(type); } -MODULE_HEADER(module_initialize, module_start, module_loop, module_stop); +MODULE_HEADER(module_initialize, module_start, module_stop); diff --git a/src/modules/funcmd.mod/module.c b/src/modules/funcmd.mod/module.c index 667b6d1..df308a5 100644 --- a/src/modules/funcmd.mod/module.c +++ b/src/modules/funcmd.mod/module.c @@ -27,12 +27,8 @@ static void module_start(int type) { } -static void module_loop() { - -} - static void module_stop(int type) { } -MODULE_HEADER(module_initialize, module_start, module_loop, module_stop); +MODULE_HEADER(module_initialize, module_start, module_stop); diff --git a/src/modules/global.mod/cmd_global_reconnect.c b/src/modules/global.mod/cmd_global_reconnect.c index 84eafa3..1bd3aae 100644 --- a/src/modules/global.mod/cmd_global_reconnect.c +++ b/src/modules/global.mod/cmd_global_reconnect.c @@ -35,13 +35,13 @@ CMD_BIND(global_cmd_reconnect) { botid = atoi(row[0]); for(client = getBots(0, NULL); client; client = getBots(0, client)) { if(client->clientid == botid) { - disconnect_socket(client); - client->flags |= SOCKET_FLAG_FAST_JUMP; + close_socket(client); + connect_socket(client); break; } } } else { - disconnect_socket(client); + close_socket(client); connect_socket(client); } reply(textclient, user, "NS_RECONNECT_DONE"); diff --git a/src/modules/global.mod/module.c b/src/modules/global.mod/module.c index b4dc94f..a2c5bee 100644 --- a/src/modules/global.mod/module.c +++ b/src/modules/global.mod/module.c @@ -26,12 +26,8 @@ static void module_start(int type) { } -static void module_loop() { - -} - static void module_stop(int type) { } -MODULE_HEADER(module_initialize, module_start, module_loop, module_stop); +MODULE_HEADER(module_initialize, module_start, module_stop); diff --git a/src/modules/module.h b/src/modules/module.h index 50207b5..5e203a7 100644 --- a/src/modules/module.h +++ b/src/modules/module.h @@ -58,9 +58,9 @@ extern int module_id; /* 028 */ #define getChannelUsers ((struct ChanUser* (*)(struct ChanNode *, struct ChanUser *))global[28]) /* 029 */ #define getUserChannels ((struct ChanUser* (*)(struct UserNode *, struct ChanUser *))global[29]) /* 030 */ #define create_socket ((struct ClientSocket* (*)(char *, int, char *, char *, char *, char *, char *))global[30]) -/* 031 */ #define connect_socket ((int (*)(struct ClientSocket *))global[31]) +/* 031 */ #define connect_socket ((void (*)(struct ClientSocket *))global[31]) /* 032 */ #define close_socket ((int (*)(struct ClientSocket *))global[32]) -/* 033 */ #define disconnect_socket ((int (*)(struct ClientSocket *))global[33]) +/* 033 */ #define destroy_socket ((int (*)(struct ClientSocket *))global[33]) /* 034 */ #define write_socket ((int (*)(struct ClientSocket *, char*, int))global[34]) /* 035 */ #define putsock ((void (*)(struct ClientSocket *, const char *, ...))global[35]) /* 036 */ #define getBots ((struct ClientSocket* (*)(int, struct ClientSocket *))global[36]) @@ -225,7 +225,7 @@ extern int module_id; /* 193 */ #define module_neonbackup_recover_chan ((void (*)(struct ChanNode *))global[193]) /* 194 */ #define requestInvite ((void (*)(struct UserNode *, struct ChanNode *))global[194]) -#define MODULE_HEADER(initfunc,startfunc,loopfunc,stopfunc) \ +#define MODULE_HEADER(initfunc,startfunc,stopfunc) \ void **global = NULL; \ int module_id = 0; \ int init_module(void **functions, int modid) { \ @@ -236,9 +236,6 @@ extern int module_id; void start_module(int type) { \ startfunc(type); \ } \ - void loop_module() { \ - loopfunc(); \ - } \ void stop_module(int type) { \ stopfunc(type); \ } \ diff --git a/src/modules/stats.mod/module.c b/src/modules/stats.mod/module.c index f276a0a..fa7d201 100644 --- a/src/modules/stats.mod/module.c +++ b/src/modules/stats.mod/module.c @@ -39,10 +39,6 @@ static void module_start(int type) { timeq_add_name("stats", 60, module_id, stats_timer_callback, NULL); } -static void module_loop() { - -} - static void module_stop(int type) { timeq_del_name("stats"); } @@ -164,4 +160,4 @@ static TIMEQ_CALLBACK(stats_timer_callback) { close(sock); } -MODULE_HEADER(module_initialize, module_start, module_loop, module_stop); +MODULE_HEADER(module_initialize, module_start, module_stop); diff --git a/src/overall.h b/src/overall.h index c5da71b..d84c1d2 100644 --- a/src/overall.h +++ b/src/overall.h @@ -137,7 +137,7 @@ #define BOTWAR_DETECTION_TIME 7 #define BOTWAR_DETECTION_EVENTS 6 #define REWHO_TIMEOUT 10 /* wait 10 seconds before WHO an unauthed user again */ -#define TICKS_PER_SECOND 10 +#define CLEAR_CACHE_INTERVAL 10 //valid nick chars #define VALID_NICK_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890{|}~[\\]^-_`" diff --git a/src/signal.c b/src/signal.c index 9acfd2c..175f611 100644 --- a/src/signal.c +++ b/src/signal.c @@ -18,6 +18,7 @@ #include "bots.h" #include "ChanNode.h" #include "ClientSocket.h" +#include "IOHandler.h" #include "ConfigParser.h" static void sigcrash(); @@ -85,8 +86,9 @@ static void sigcrash(int signum) { struct ClientSocket *bot; for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { if((bot->flags & SOCKET_FLAG_CONNECTED)) { - close(bot->sock); + iohandler_close(bot->iofd); bot->flags &= ~(SOCKET_FLAG_CONNECTED | SOCKET_FLAG_READY); + bot->iofd = NULL; } } putlog(LOGLEVEL_INFO, "hard shutdown...\n"); diff --git a/src/ssl.c b/src/ssl.c deleted file mode 100644 index f65533c..0000000 --- a/src/ssl.c +++ /dev/null @@ -1,71 +0,0 @@ -/* ssl.c - NeonServ v5.5 - * 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 "ssl.h" -#include "ClientSocket.h" - -void ssl_init() { -#ifdef HAVE_SSL - SSL_library_init(); - SSL_load_error_strings(); -#endif -} - -void ssl_connect(struct ClientSocket *client) { -#ifdef HAVE_SSL - client->sslconn = NULL; - if(!(client->flags & SOCKET_FLAG_CONNECTED)) return; - struct SSLConnection *sslconn = malloc(sizeof(*sslconn)); - sslconn->sslContext = SSL_CTX_new(SSLv23_client_method()); - if(!sslconn->sslContext) goto ssl_connect_err; - sslconn->sslHandle = SSL_new(sslconn->sslContext); - if(!sslconn->sslHandle) goto ssl_connect_err; - if(!SSL_set_fd(sslconn->sslHandle, client->sock)) goto ssl_connect_err; - if(SSL_connect(sslconn->sslHandle) != 1) goto ssl_connect_err; - client->sslconn = sslconn; - return; -ssl_connect_err: - free(sslconn); -#endif -} - -void ssl_disconnect(struct ClientSocket *client) { -#ifdef HAVE_SSL - if(!client->sslconn) return; - SSL_shutdown(client->sslconn->sslHandle); - SSL_free(client->sslconn->sslHandle); - SSL_CTX_free(client->sslconn->sslContext); - free(client->sslconn); - client->sslconn = NULL; -#endif -} - -int ssl_read(struct ClientSocket *client, char *buffer, int len) { -#ifdef HAVE_SSL - if(!client->sslconn) return -2; - return SSL_read(client->sslconn->sslHandle, buffer, len); -#endif - return -2; -} - -int ssl_write(struct ClientSocket *client, char *buffer, int len) { -#ifdef HAVE_SSL - if(!client->sslconn) return -2; - return SSL_write(client->sslconn->sslHandle, buffer, len); -#endif - return -2; -} \ No newline at end of file diff --git a/src/ssl.h b/src/ssl.h deleted file mode 100644 index 0af8ad0..0000000 --- a/src/ssl.h +++ /dev/null @@ -1,45 +0,0 @@ -/* ssl.h - NeonServ v5.5 - * 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 . - */ -#ifndef _ssl_h -#define _ssl_h - -#include "main.h" - -struct ClientSocket; - -#ifdef HAVE_SSL -#include -#include -#include - -struct SSLConnection { - SSL *sslHandle; - SSL_CTX *sslContext; -}; -#else -struct SSLConnection { - //just unused -}; -#endif - -void ssl_init(); -void ssl_connect(struct ClientSocket *client); -void ssl_disconnect(struct ClientSocket *client); -int ssl_read(struct ClientSocket *client, char *buffer, int len); -int ssl_write(struct ClientSocket *client, char *buffer, int len); - -#endif \ No newline at end of file diff --git a/src/test/Makefile b/src/test/Makefile new file mode 100644 index 0000000..df861f0 --- /dev/null +++ b/src/test/Makefile @@ -0,0 +1,12 @@ + +CC = gcc +CFLAGS = -g -O0 -Wall -Wshadow -Werror +LDFLAGS = -lws2_32 + +OBJ = ../IOEngine_epoll.o ../IOEngine_kevent.o ../IOEngine_select.o ../IOEngine_win32.o ../IOHandler.o ../IOHandler_SSL.o iotest.o + +all: $(OBJ) + $(CC) $(CFLAGS) -oiotest $(OBJ) $(LDFLAGS) + +%.o: %.c + $(CC) $(CFLAGS) -o$@ -c $< diff --git a/src/test/iotest.c b/src/test/iotest.c new file mode 100644 index 0000000..15f32f4 --- /dev/null +++ b/src/test/iotest.c @@ -0,0 +1,63 @@ +/* main.c - IOMultiplexer + * Copyright (C) 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 +#include "../IOHandler.h" + +static IOHANDLER_CALLBACK(io_callback); +static IOHANDLER_LOG_BACKEND(io_log); + +static struct IODescriptor *irc_iofd = NULL; + +int main(int argc, char *argv[]) { + iolog_backend = io_log; + + irc_iofd = iohandler_connect("pk910.de", 6667, 0, NULL, io_callback); + + struct IODescriptor *iofd; + iofd = iohandler_add(0, IOTYPE_STDIN, NULL, io_callback); + iofd->read_lines = 1; + + while(1) { + iohandler_poll(); + } +} + +static IOHANDLER_CALLBACK(io_callback) { + switch(event->type) { + case IOEVENT_CONNECTED: + printf("[connect]\n"); + break; + case IOEVENT_CLOSED: + printf("[disconnect]\n"); + break; + case IOEVENT_RECV: + if(event->iofd->type == IOTYPE_STDIN) { + iohandler_printf(irc_iofd, "%s\n", event->data.recv_str); + printf("[out] %s\n", event->data.recv_str); + } else + printf("[in] %s\n", event->data.recv_str); + break; + + default: + break; + } +} + +static IOHANDLER_LOG_BACKEND(io_log) { + //printf("%s", line); +} diff --git a/src/timeq.c b/src/timeq.c index be82f8f..b53b58d 100644 --- a/src/timeq.c +++ b/src/timeq.c @@ -16,34 +16,24 @@ */ #include "timeq.h" +#include "IOHandler.h" static struct timeq_entry *timeq_events; #ifdef HAVE_THREADS +static int pthread_mutex_initialized = 0; static pthread_mutex_t synchronized; #endif -void init_timeq() { - THREAD_MUTEX_INIT(synchronized); -} - -void timeq_tick() { - SYNCHRONIZE(synchronized); - struct timeq_entry *entry, *next; - struct timeval now; - gettimeofday(&now, NULL); - for(entry = timeq_events; entry; entry = next) { - if(!timeval_is_bigger(entry->execute, now)) { - next = entry->next; - if(timeq_events == entry) - timeq_events = next; - entry->callback(entry->data); - if(entry->name) - free(entry->name); - free(entry); - } else - break; +static IOHANDLER_CALLBACK(timeq_callback) { + struct timeq_entry *entry = event->iofd->data; + switch(event->type) { + case IOEVENT_TIMEOUT: + entry->callback(entry->data); + timeq_del(entry); + break; + default: + break; } - DESYNCHRONIZE(synchronized); } struct timeq_entry* timeq_add(int seconds, int module_id, timeq_callback_t *callback, void *data) { @@ -51,36 +41,31 @@ struct timeq_entry* timeq_add(int seconds, int module_id, timeq_callback_t *call } struct timeq_entry* timeq_uadd(int useconds, int module_id, timeq_callback_t *callback, void *data) { - struct timeval now; - gettimeofday(&now, NULL); + struct timeval timeout; struct timeq_entry *entry = malloc(sizeof(*entry)); if (!entry) { perror("malloc() failed"); return NULL; } + if(!pthread_mutex_initialized) { + THREAD_MUTEX_INIT(synchronized); + pthread_mutex_initialized = 1; + } + gettimeofday(&timeout, NULL); SYNCHRONIZE(synchronized); - now.tv_usec += (useconds % 1000); - now.tv_sec += (useconds / 1000); - entry->execute = now; + timeout.tv_usec += (useconds % 1000); + timeout.tv_sec += (useconds / 1000); + entry->iofd = iohandler_timer(timeout, timeq_callback); + entry->iofd->data = entry; entry->module_id = module_id; entry->callback = callback; entry->data = data; entry->name = NULL; - struct timeq_entry *next, *prev = NULL; - for(next = timeq_events; next; next = next->next) { - if(timeval_is_bigger(next->execute, now)) - break; - else - prev = next; - } - if(prev == NULL) { - entry->next = timeq_events; - timeq_events = entry; - } else { - entry->next = next; - prev->next = entry; - } + entry->next = timeq_events; + entry->prev = NULL; + timeq_events->prev = entry; + timeq_events = entry; DESYNCHRONIZE(synchronized); return entry; } @@ -90,54 +75,40 @@ struct timeq_entry* timeq_add_name(char *name, int seconds, int module_id, timeq } struct timeq_entry* timeq_uadd_name(char *name, int useconds, int module_id, timeq_callback_t *callback, void *data) { - SYNCHRONIZE(synchronized); struct timeq_entry *entry = timeq_uadd(useconds, module_id, callback, data); entry->name = strdup(name); - DESYNCHRONIZE(synchronized); return entry; } int timeq_del(struct timeq_entry* entry) { + if(!pthread_mutex_initialized) return 0; SYNCHRONIZE(synchronized); - struct timeq_entry *centry, *last = NULL; - for(centry = timeq_events; centry; centry = centry->next) { - if(centry == entry) { - if(last) - last->next = centry->next; - else - timeq_events = centry->next; - if(centry->name) - free(centry->name); - free(centry); - DESYNCHRONIZE(synchronized); - return 1; - } else { - last = centry; - } - } + if(entry->next) + entry->next->prev = entry->prev; + if(entry->prev) + entry->prev->next = entry->next; + else + timeq_events = entry->next; + if(entry->name) + free(entry->name); + iohandler_close(entry->iofd); + free(entry); DESYNCHRONIZE(synchronized); - return 0; + return 1; } int timeq_del_name(char *name) { SYNCHRONIZE(synchronized); - struct timeq_entry *centry, *last = NULL; - for(centry = timeq_events; centry; centry = centry->next) { - if(centry->name && !stricmp(centry->name, name)) { - if(last) - last->next = centry->next; - else - timeq_events = centry->next; - free(centry->name); - free(centry); - DESYNCHRONIZE(synchronized); - return 1; - } else { - last = centry; + struct timeq_entry *entry; + int removed = 0; + for(entry = timeq_events; entry; entry = entry->next) { + if(entry->name && !stricmp(entry->name, name)) { + removed = timeq_del(entry); + break; } } DESYNCHRONIZE(synchronized); - return 0; + return removed; } int timeq_name_exists(char *name) { @@ -155,18 +126,11 @@ int timeq_name_exists(char *name) { void unregister_module_timers(int module_id) { SYNCHRONIZE(synchronized); - struct timeq_entry *centry, *next, *last = NULL; - for(centry = timeq_events; centry; centry = next) { - next = centry->next; - if(centry->module_id == module_id) { - if(last) - last->next = centry->next; - else - timeq_events = centry->next; - free(centry->name); - free(centry); - } else - last = centry; + struct timeq_entry *entry, *next_entry; + for(entry = timeq_events; entry; entry = next_entry) { + next_entry = entry->next; + if(entry->module_id == module_id) + timeq_del(entry); } DESYNCHRONIZE(synchronized); } diff --git a/src/timeq.h b/src/timeq.h index 989021b..969a294 100644 --- a/src/timeq.h +++ b/src/timeq.h @@ -22,19 +22,19 @@ #define TIMEQ_CALLBACK(NAME) void NAME(UNUSED_ARG(void *data)) typedef TIMEQ_CALLBACK(timeq_callback_t); +struct IODescriptor; + struct timeq_entry { + struct IODescriptor *iofd; char *name; int module_id; - struct timeval execute; timeq_callback_t *callback; void *data; - struct timeq_entry *next; + struct timeq_entry *prev, *next; }; #ifndef DND_FUNCTIONS -void init_timeq(); -void timeq_tick(); /* MODULAR ACCESSIBLE */ struct timeq_entry* timeq_add(int seconds, int module_id, timeq_callback_t *callback, void *data); /* MODULAR ACCESSIBLE */ struct timeq_entry* timeq_uadd(int useconds, int module_id, timeq_callback_t *callback, void *data); /* MODULAR ACCESSIBLE */ struct timeq_entry* timeq_add_name(char *name, int seconds, int module_id, timeq_callback_t *callback, void *data);