X-Git-Url: http://git.pk910.de/?p=TransparentIRC.git;a=blobdiff_plain;f=src%2FIOHandler.c;h=7a9f3b3d30e7aa4b098c7c83cae4dd031dbbe3dc;hp=1843accb8f9cde95a5b25daab8827ce8677b3c10;hb=79b5ee62665a460214046003cdbe1b4b4f1fa39e;hpb=9d6d43ef738ec4ea700d1ab62bae892d69bede8a diff --git a/src/IOHandler.c b/src/IOHandler.c index 1843acc..7a9f3b3 100644 --- a/src/IOHandler.c +++ b/src/IOHandler.c @@ -17,33 +17,135 @@ #include "IOHandler.h" #include "IOEngine.h" +#define MAXLOG 1024 + struct IODescriptor *first_descriptor = NULL; +struct IODescriptor *timer_priority = NULL; + +void iohandler_log(enum IOLogType type, char *text, ...) { + va_list arg_list; + char logBuf[MAXLOG+1]; + int pos; + logBuf[0] = '\0'; + va_start(arg_list, text); + pos = vsnprintf(logBuf, MAXLOG - 1, text, arg_list); + va_end(arg_list); + if (pos < 0 || pos > (MAXLOG - 1)) pos = MAXLOG - 1; + logBuf[pos] = '\n'; + logBuf[pos+1] = '\0'; + + //do something with logBuf + //... +} /* IO Engines */ extern struct IOEngine engine_select; /* select system call (should always be useable) */ +extern struct IOEngine engine_kevent; +extern struct IOEngine engine_epoll; struct IOEngine *engine = NULL; static void iohandler_init_engine() { //try other engines + if(!engine && engine_kevent.init && engine_kevent.init()) + engine = &engine_kevent; + if(!engine && engine_epoll.init && engine_epoll.init()) + engine = &engine_epoll; - if(engine) { - //found an useable IO engine - } else if (engine_select.init()) - engine = &engine_select; - else { - //STDERR: found no useable IO engine + if (!engine) { + if(engine_select.init()) + engine = &engine_select; + else { + iohandler_log(IOLOG_FATAL, "found no useable IO engine"); + return; + } + } + iohandler_log(IOLOG_DEBUG, "using %s IO engine", engine->name); +} + +static void iohandler_append(struct IODescriptor *descriptor) { + struct timeval *timeout = ((descriptor->timeout.tv_sec || descriptor->timeout.tv_usec) ? &descriptor->timeout : NULL); + if(timeout) { + struct IODescriptor *iofd; + int set_priority = 1; + descriptor->timeout = *timeout; + if(timer_priority) + iofd = timer_priority; + else + iofd = first_descriptor; + if(iofd) { + for(;;iofd = iofd->next) { + if(timeval_is_smaler(timeout, (&iofd->timeout))) { + descriptor->prev = iofd->prev; + descriptor->next = iofd; + iofd->prev = descriptor; + if(iofd->prev) + iofd->prev->next = descriptor; + if(set_priority) + timer_priority = descriptor; + break; + } + if(iofd == timer_priority) + set_priority = 0; + if(iofd->next == NULL) { + descriptor->next = NULL; + descriptor->prev = iofd; + iofd->next = descriptor; + if(set_priority) + timer_priority = descriptor; + break; + } + } + } else { + descriptor->prev = NULL; + descriptor->next = NULL; + first_descriptor = descriptor; + timer_priority = descriptor; + } + + } else { + descriptor->prev = NULL; + descriptor->next = first_descriptor; + if(first_descriptor) + first_descriptor->prev = descriptor; + first_descriptor = descriptor; } } -struct IODescriptor *iohandler_add(int sockfd, enum IOType type, iohandler_callback *callback) { +static void iohandler_remove(struct IODescriptor *descriptor, int engine_remove) { + //remove IODescriptor from the list + if(descriptor->prev) + descriptor->prev->next = descriptor->next; + else + first_descriptor = descriptor->next; + if(descriptor->next) + descriptor->next->prev = descriptor->prev; + if(descriptor == timer_priority) + timer_priority = descriptor->next; + + if(engine_remove) + engine->remove(descriptor); + if(descriptor->readbuf.buffer) + free(descriptor->readbuf.buffer); + if(descriptor->writebuf.buffer) + free(descriptor->writebuf.buffer); + iohandler_log(IOLOG_DEBUG, "removed IODescriptor (%d) of type `%s`", descriptor->fd, iohandler_iotype_name(descriptor->type)); + free(descriptor); +} + +struct IODescriptor *iohandler_add(int sockfd, enum IOType type, struct timeval *timeout, iohandler_callback *callback) { //just add a new IODescriptor struct IODescriptor *descriptor = calloc(1, sizeof(*descriptor)); - if(!descriptor) return NULL; + if(!descriptor) { + iohandler_log(IOLOG_ERROR, "could not allocate memory for IODescriptor in %s:%d", __FILE__, __LINE__); + return NULL; + } descriptor->fd = sockfd; descriptor->type = type; descriptor->state = IO_CLOSED; descriptor->callback = callback; + if(timeout) + descriptor->timeout = *timeout; if(type != IOTYPE_TIMER) { descriptor->readbuf.buffer = malloc(IO_READ_BUFLEN + 2); descriptor->readbuf.bufpos = 0; @@ -62,30 +164,28 @@ struct IODescriptor *iohandler_add(int sockfd, enum IOType type, iohandler_callb engine->add(descriptor); //add IODescriptor to the list - descriptor->prev = NULL; - descriptor->next = first_descriptor; - if(first_descriptor) - first_descriptor->prev = descriptor; - first_descriptor = descriptor; + iohandler_append(descriptor); + iohandler_log(IOLOG_DEBUG, "added custom socket descriptor (%d) as type `%s`", sockfd, iohandler_iotype_name(type)); return descriptor; } -static void iohandler_remove(struct IODescriptor *descriptor, int engine_remove) { - //remove IODescriptor from the list +void iohandler_set_timeout(struct IODescriptor *descriptor, struct timeval *timeout) { if(descriptor->prev) descriptor->prev->next = descriptor->next; else first_descriptor = descriptor->next; if(descriptor->next) descriptor->next->prev = descriptor->prev; - if(engine_remove) - engine->remove(descriptor); - if(descriptor->readbuf.buffer) - free(descriptor->readbuf.buffer); - if(descriptor->writebuf.buffer) - free(descriptor->writebuf.buffer); - free(descriptor); + if(descriptor == timer_priority) + timer_priority = descriptor->next; + if(timeout) + descriptor->timeout = *timeout; + else { + descriptor->timeout.tv_sec = 0; + descriptor->timeout.tv_usec = 0; + } + iohandler_append(descriptor); } static void iohandler_increase_iobuf(struct IOBuffer *iobuf, size_t required) { @@ -99,10 +199,12 @@ static void iohandler_increase_iobuf(struct IOBuffer *iobuf, size_t required) { struct IODescriptor *iohandler_timer(struct timeval timeout, iohandler_callback *callback) { struct IODescriptor *descriptor; - descriptor = iohandler_add(-1, IOTYPE_TIMER, callback); - if(!descriptor) return NULL; - descriptor->timeout = timeout; - engine->update(descriptor); + descriptor = iohandler_add(-1, IOTYPE_TIMER, &timeout, callback); + if(!descriptor) { + iohandler_log(IOLOG_ERROR, "could not allocate memory for IODescriptor in %s:%d", __FILE__, __LINE__); + return NULL; + } + iohandler_log(IOLOG_DEBUG, "added timer descriptor (sec: %d; usec: %d)", timeout.tv_sec, timeout.tv_usec); return descriptor; } @@ -121,6 +223,7 @@ struct IODescriptor *iohandler_connect(const char *hostname, unsigned int port, hints.ai_socktype = SOCK_STREAM; hints.ai_flags |= AI_CANONNAME; if (getaddrinfo (hostname, NULL, &hints, &res)) { + iohandler_log(IOLOG_ERROR, "could not resolve %d to an IP address", hostname); return NULL; } while (res) { @@ -139,7 +242,10 @@ struct IODescriptor *iohandler_connect(const char *hostname, unsigned int port, if(ip6) { sockfd = socket(AF_INET6, SOCK_STREAM, 0); - if(sockfd == -1) return NULL; + if(sockfd == -1) { + iohandler_log(IOLOG_ERROR, "could not create socket in %s:%d", __FILE__, __LINE__); + return NULL; + } ip6->sin6_family = AF_INET6; ip6->sin6_port = htons(port); @@ -166,7 +272,10 @@ struct IODescriptor *iohandler_connect(const char *hostname, unsigned int port, dstaddrlen = sizeof(*ip6); } else if(ip4) { sockfd = socket(AF_INET, SOCK_STREAM, 0); - if(sockfd == -1) return NULL; + if(sockfd == -1) { + iohandler_log(IOLOG_ERROR, "could not create socket in %s:%d", __FILE__, __LINE__); + return NULL; + } ip4->sin_family = AF_INET; ip4->sin_port = htons(port); @@ -195,16 +304,19 @@ struct IODescriptor *iohandler_connect(const char *hostname, unsigned int port, return NULL; //make sockfd unblocking #if defined(F_GETFL) - flags = fcntl(sockfd, F_GETFL); - fcntl(sockfd, F_SETFL, flags|O_NONBLOCK); - flags = fcntl(sockfd, F_GETFD); - fcntl(sockfd, F_SETFD, flags|FD_CLOEXEC); + { + int flags; + flags = fcntl(sockfd, F_GETFL); + fcntl(sockfd, F_SETFL, flags|O_NONBLOCK); + flags = fcntl(sockfd, F_GETFD); + fcntl(sockfd, F_SETFD, flags|FD_CLOEXEC); + } #else /* I hope you're using the Win32 backend or something else that * automatically marks the file descriptor non-blocking... */ #endif - descriptor = iohandler_add(sockfd, IOTYPE_CLIENT, callback); + descriptor = iohandler_add(sockfd, IOTYPE_CLIENT, NULL, callback); if(!descriptor) { close(sockfd); return NULL; @@ -213,6 +325,7 @@ struct IODescriptor *iohandler_connect(const char *hostname, unsigned int port, descriptor->state = IO_CONNECTING; descriptor->read_lines = 1; engine->update(descriptor); + iohandler_log(IOLOG_DEBUG, "added client socket (%d) connecting to %s:%d", sockfd, hostname, port); return descriptor; } @@ -271,16 +384,19 @@ struct IODescriptor *iohandler_listen(const char *hostname, unsigned int port, i return NULL; //make sockfd unblocking #if defined(F_GETFL) - flags = fcntl(sockfd, F_GETFL); - fcntl(sockfd, F_SETFL, flags|O_NONBLOCK); - flags = fcntl(sockfd, F_GETFD); - fcntl(sockfd, F_SETFD, flags|FD_CLOEXEC); + { + int flags; + flags = fcntl(sockfd, F_GETFL); + fcntl(sockfd, F_SETFL, flags|O_NONBLOCK); + flags = fcntl(sockfd, F_GETFD); + fcntl(sockfd, F_SETFD, flags|FD_CLOEXEC); + } #else /* I hope you're using the Win32 backend or something else that * automatically marks the file descriptor non-blocking... */ #endif - descriptor = iohandler_add(sockfd, IOTYPE_SERVER, callback); + descriptor = iohandler_add(sockfd, IOTYPE_SERVER, NULL, callback); if(!descriptor) { close(sockfd); return NULL; @@ -288,6 +404,7 @@ struct IODescriptor *iohandler_listen(const char *hostname, unsigned int port, i listen(sockfd, 1); descriptor->state = IO_LISTENING; engine->update(descriptor); + iohandler_log(IOLOG_DEBUG, "added server socket (%d) listening on %s:%d", sockfd, hostname, port); return descriptor; } @@ -297,11 +414,18 @@ void iohandler_write(struct IODescriptor *iofd, const char *line) { } void iohandler_send(struct IODescriptor *iofd, const char *data, size_t datalen) { - if(iofd->type == IOTYPE_TIMER || iofd->state == IO_CLOSED) return; //can not write to timer? :D + if(iofd->type == IOTYPE_TIMER || iofd->state == IO_CLOSED) { + iohandler_log(IOLOG_ERROR, "could not write to socket (%s)", (iofd->type == IOTYPE_TIMER ? "IOTYPE_TIMER" : "IO_CLOSED")); + return; + } + iohandler_log(IOLOG_DEBUG, "add %d to writebuf (fd: %d): %s", datalen, iofd->fd, data); if(iofd->writebuf.buflen < iofd->writebuf.bufpos + datalen) { + iohandler_log(IOLOG_DEBUG, "increase writebuf (curr: %d) to %d (+%d bytes)", iofd->writebuf.buflen, iofd->writebuf.bufpos + datalen, (iofd->writebuf.bufpos + datalen - iofd->writebuf.buflen)); iohandler_increase_iobuf(&iofd->writebuf, iofd->writebuf.bufpos + datalen); - if(iofd->writebuf.buflen < iofd->writebuf.bufpos + datalen) + if(iofd->writebuf.buflen < iofd->writebuf.bufpos + datalen) { + iohandler_log(IOLOG_ERROR, "increase writebuf (curr: %d) to %d (+%d bytes) FAILED", iofd->writebuf.buflen, iofd->writebuf.bufpos + datalen, (iofd->writebuf.bufpos + datalen - iofd->writebuf.buflen)); return; + } } memcpy(iofd->writebuf.buffer + iofd->writebuf.bufpos, data, datalen); iofd->writebuf.bufpos += datalen; @@ -324,26 +448,32 @@ void iohandler_printf(struct IODescriptor *iofd, const char *text, ...) { void iohandler_try_write(struct IODescriptor *iofd) { if(!iofd->writebuf.bufpos) return; + iohandler_log(IOLOG_DEBUG, "write writebuf (%d bytes) to socket (fd: %d)", iofd->writebuf.bufpos, iofd->fd); int res = send(iofd->fd, iofd->writebuf.buffer, iofd->writebuf.bufpos, 0); if(res < 0) { if (errno != EAGAIN) { - //error: could not write + iohandler_log(IOLOG_ERROR, "could not write to socket (fd: %d): %d - %s", iofd->fd, errno, strerror(errno)); } } else { iofd->writebuf.bufpos -= res; - engine->update(iofd); + if(iofd->state != IO_CLOSED) + engine->update(iofd); } } void iohandler_close(struct IODescriptor *iofd) { int engine_remove = 1; + iofd->state = IO_CLOSED; if(iofd->writebuf.bufpos) { //try to send everything before closing #if defined(F_GETFL) - flags = fcntl(sockfd, F_GETFL); - fcntl(sockfd, F_SETFL, flags & ~O_NONBLOCK); - flags = fcntl(sockfd, F_GETFD); - fcntl(sockfd, F_SETFD, flags|FD_CLOEXEC); + { + int flags; + flags = fcntl(iofd->fd, F_GETFL); + fcntl(iofd->fd, F_SETFL, flags & ~O_NONBLOCK); + flags = fcntl(iofd->fd, F_GETFD); + fcntl(iofd->fd, F_SETFD, flags|FD_CLOEXEC); + } #else engine_remove = 0; engine->remove(iofd); @@ -357,11 +487,13 @@ void iohandler_close(struct IODescriptor *iofd) { } void iohandler_update(struct IODescriptor *iofd) { + iohandler_log(IOLOG_DEBUG, "external call to iohandler_update (fd: %d)", iofd->fd); engine->update(iofd); } static void iohandler_trigger_event(struct IOEvent *event) { if(!event->iofd->callback) return; + iohandler_log(IOLOG_DEBUG, "triggering event (%s) for %s (fd: %d)", iohandler_ioeventtype_name(event->type), iohandler_iotype_name(event->iofd->type), event->iofd->fd); event->iofd->callback(event); } @@ -378,7 +510,7 @@ void iohandler_events(struct IODescriptor *iofd, int readable, int writeable) { if(readable) { callback_event.data.accept_fd = accept(iofd->fd, NULL, 0); if(callback_event.data.accept_fd < 0) { - //error: could not accept + iohandler_log(IOLOG_ERROR, "could not accept client (server fd: %d): %d - %s", iofd->fd, errno, strerror(errno)); } else callback_event.type = IOEVENT_ACCEPT; } @@ -391,6 +523,7 @@ void iohandler_events(struct IODescriptor *iofd, int readable, int writeable) { if (getsockopt(iofd->fd, SOL_SOCKET, SO_ERROR, &callback_event.data.errid, &arglen) < 0) callback_event.data.errid = errno; iofd->state = IO_CLOSED; + engine->update(iofd); } else if(writeable) { callback_event.type = IOEVENT_CONNECTED; iofd->state = IO_CONNECTED; @@ -404,11 +537,13 @@ void iohandler_events(struct IODescriptor *iofd, int readable, int writeable) { if(bytes <= 0) { if (errno != EAGAIN) { iofd->state = IO_CLOSED; + engine->update(iofd); callback_event.type = IOEVENT_CLOSED; callback_event.data.errid = errno; } } else { int i, used_bytes = 0; + iohandler_log(IOLOG_DEBUG, "received %d bytes (fd: %d). readbuf position: %d", bytes, iofd->fd, iofd->readbuf.bufpos); iofd->readbuf.bufpos += bytes; callback_event.type = IOEVENT_RECV; for(i = 0; i < iofd->readbuf.bufpos; i++) { @@ -417,11 +552,13 @@ void iohandler_events(struct IODescriptor *iofd, int readable, int writeable) { else if(iofd->readbuf.buffer[i] == '\n' || iofd->readbuf.buffer[i] == '\r') { iofd->readbuf.buffer[i] = 0; callback_event.data.recv_str = iofd->readbuf.buffer + used_bytes; + iohandler_log(IOLOG_DEBUG, "parsed line (%d bytes): %s", i - used_bytes, iofd->readbuf.buffer + used_bytes); used_bytes = i+1; iohandler_trigger_event(&callback_event); } else if(i + 1 - used_bytes >= LINELEN) { //512 max iofd->readbuf.buffer[i] = 0; callback_event.data.recv_str = iofd->readbuf.buffer + used_bytes; + iohandler_log(IOLOG_DEBUG, "parsed and stripped line (%d bytes): %s", i - used_bytes, iofd->readbuf.buffer + used_bytes); for(; i < iofd->readbuf.bufpos; i++) { //skip the rest of the line if(iofd->readbuf.buffer[i] == '\n' || (iofd->readbuf.buffer[i] == '\r' && iofd->readbuf.buffer[i+1] != '\n')) { break; @@ -432,9 +569,11 @@ void iohandler_events(struct IODescriptor *iofd, int readable, int writeable) { } } if(used_bytes) { - if(used_bytes == iofd->readbuf.bufpos) + if(used_bytes == iofd->readbuf.bufpos) { iofd->readbuf.bufpos = 0; - else { + iohandler_log(IOLOG_DEBUG, "readbuf fully processed (set buffer position to 0)"); + } else { + iohandler_log(IOLOG_DEBUG, "readbuf rest: %d bytes (used %d bytes)", iofd->readbuf.bufpos - used_bytes, used_bytes); memmove(iofd->readbuf.buffer, iofd->readbuf.buffer + used_bytes, iofd->readbuf.bufpos - used_bytes); iofd->readbuf.bufpos -= used_bytes; } @@ -464,3 +603,58 @@ void iohandler_poll() { } } +//debugging functions +char *iohandler_iotype_name(enum IOType type) { + switch(type) { + case IOTYPE_UNKNOWN: + return "IOTYPE_UNKNOWN"; + case IOTYPE_SERVER: + return "IOTYPE_SERVER"; + case IOTYPE_CLIENT: + return "IOTYPE_CLIENT"; + case IOTYPE_STDIN: + return "IOTYPE_STDIN"; + case IOTYPE_TIMER: + return "IOTYPE_TIMER"; + default: + return "(UNDEFINED)"; + } +} + +char *iohandler_iostatus_name(enum IOStatus status) { + switch(status) { + case IO_CLOSED: + return "IO_CLOSED"; + case IO_LISTENING: + return "IO_LISTENING"; + case IO_CONNECTING: + return "IO_CONNECTING"; + case IO_CONNECTED: + return "IO_CONNECTED"; + default: + return "(UNDEFINED)"; + } +} + +char *iohandler_ioeventtype_name(enum IOEventType type) { + switch(type) { + case IOEVENT_IGNORE: + return "IOEVENT_IGNORE"; + case IOEVENT_READABLE: + return "IOEVENT_READABLE"; + case IOEVENT_RECV: + return "IOEVENT_RECV"; + case IOEVENT_CONNECTED: + return "IOEVENT_CONNECTED"; + case IOEVENT_NOTCONNECTED: + return "IOEVENT_NOTCONNECTED"; + case IOEVENT_CLOSED: + return "IOEVENT_CLOSED"; + case IOEVENT_ACCEPT: + return "IOEVENT_ACCEPT"; + case IOEVENT_TIMEOUT: + return "IOEVENT_TIMEOUT"; + default: + return "(UNDEFINED)"; + } +}