modified IOMultiplexer (added epoll & kevent support)
[TransparentIRC.git] / src / IOHandler.c
1 /* IOHandler.c - TransparentIRC 0.1
2  * Copyright (C) 2011-2012  Philipp Kreil (pk910)
3  * 
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  * 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  * 
14  * You should have received a copy of the GNU General Public License 
15  * along with this program. If not, see <http://www.gnu.org/licenses/>. 
16  */
17 #include "IOHandler.h"
18 #include "IOEngine.h"
19
20 #define MAXLOG 1024
21
22 struct IODescriptor *first_descriptor = NULL;
23 struct IODescriptor *timer_priority = NULL;
24
25 void iohandler_log(enum IOLogType type, char *text, ...) {
26     va_list arg_list;
27     char logBuf[MAXLOG+1];
28     int pos;
29     logBuf[0] = '\0';
30     va_start(arg_list, text);
31     pos = vsnprintf(logBuf, MAXLOG - 1, text, arg_list);
32     va_end(arg_list);
33     if (pos < 0 || pos > (MAXLOG - 1)) pos = MAXLOG - 1;
34     logBuf[pos] = '\n';
35     logBuf[pos+1] = '\0';
36     
37     //do something with logBuf
38     //...
39 }
40
41 /* IO Engines */
42 extern struct IOEngine engine_select; /* select system call (should always be useable) */
43 extern struct IOEngine engine_kevent;
44 extern struct IOEngine engine_epoll;
45
46 struct IOEngine *engine = NULL;
47
48 static void iohandler_init_engine() {
49     //try other engines
50     if(!engine && engine_kevent.init && engine_kevent.init())
51         engine = &engine_kevent;
52     if(!engine && engine_epoll.init && engine_epoll.init())
53         engine = &engine_epoll;
54     
55     if (!engine) {
56         if(engine_select.init())
57             engine = &engine_select;
58         else {
59             iohandler_log(IOLOG_FATAL, "found no useable IO engine");
60             return;
61         }
62     }
63     iohandler_log(IOLOG_DEBUG, "using %s IO engine", engine->name);
64 }
65
66 static void iohandler_append(struct IODescriptor *descriptor) {
67     struct timeval *timeout = ((descriptor->timeout.tv_sec || descriptor->timeout.tv_usec) ? &descriptor->timeout : NULL);
68     if(timeout) {
69         struct IODescriptor *iofd;
70         int set_priority = 1;
71         descriptor->timeout = *timeout;
72         if(timer_priority)
73             iofd = timer_priority;
74         else
75             iofd = first_descriptor;
76         if(iofd) {
77             for(;;iofd = iofd->next) {
78                 if(timeval_is_smaler(timeout, (&iofd->timeout))) {
79                     descriptor->prev = iofd->prev;
80                     descriptor->next = iofd;
81                     iofd->prev = descriptor;
82                     if(iofd->prev)
83                         iofd->prev->next = descriptor;
84                     if(set_priority)
85                         timer_priority = descriptor;
86                     break;
87                 }
88                 if(iofd == timer_priority)
89                     set_priority = 0;
90                 if(iofd->next == NULL) {
91                     descriptor->next = NULL;
92                     descriptor->prev = iofd;
93                     iofd->next = descriptor;
94                     if(set_priority)
95                         timer_priority = descriptor;
96                     break;
97                 }
98             }
99         } else {
100             descriptor->prev = NULL;
101             descriptor->next = NULL;
102             first_descriptor = descriptor;
103             timer_priority = descriptor;
104         }
105         
106     } else {
107         descriptor->prev = NULL;
108         descriptor->next = first_descriptor;
109         if(first_descriptor)
110             first_descriptor->prev = descriptor;
111         first_descriptor = descriptor;
112     }
113 }
114
115 static void iohandler_remove(struct IODescriptor *descriptor, int engine_remove) {
116     //remove IODescriptor from the list
117     if(descriptor->prev)
118         descriptor->prev->next = descriptor->next;
119     else
120         first_descriptor = descriptor->next;
121     if(descriptor->next)
122         descriptor->next->prev = descriptor->prev;
123     if(descriptor == timer_priority)
124         timer_priority = descriptor->next;
125     
126     if(engine_remove)
127         engine->remove(descriptor);
128     if(descriptor->readbuf.buffer)
129         free(descriptor->readbuf.buffer);
130     if(descriptor->writebuf.buffer)
131         free(descriptor->writebuf.buffer);
132     iohandler_log(IOLOG_DEBUG, "removed IODescriptor (%d) of type `%s`", descriptor->fd, iohandler_iotype_name(descriptor->type));
133     free(descriptor);
134 }
135
136 struct IODescriptor *iohandler_add(int sockfd, enum IOType type, struct timeval *timeout, iohandler_callback *callback) {
137     //just add a new IODescriptor
138     struct IODescriptor *descriptor = calloc(1, sizeof(*descriptor));
139     if(!descriptor) {
140         iohandler_log(IOLOG_ERROR, "could not allocate memory for IODescriptor in %s:%d", __FILE__, __LINE__);
141         return NULL;
142     }
143     descriptor->fd = sockfd;
144     descriptor->type = type;
145     descriptor->state = IO_CLOSED;
146     descriptor->callback = callback;
147     if(timeout)
148         descriptor->timeout = *timeout;
149     if(type != IOTYPE_TIMER) {
150         descriptor->readbuf.buffer = malloc(IO_READ_BUFLEN + 2);
151         descriptor->readbuf.bufpos = 0;
152         descriptor->readbuf.buflen = IO_READ_BUFLEN;
153         descriptor->writebuf.buffer = malloc(IO_READ_BUFLEN + 2);
154         descriptor->writebuf.bufpos = 0;
155         descriptor->writebuf.buflen = IO_READ_BUFLEN;
156     }
157     
158     if(!engine) {
159         iohandler_init_engine();
160         if(!engine) {
161             return NULL;
162         }
163     }
164     engine->add(descriptor);
165     
166     //add IODescriptor to the list
167     iohandler_append(descriptor);
168     
169     iohandler_log(IOLOG_DEBUG, "added custom socket descriptor (%d) as type `%s`", sockfd, iohandler_iotype_name(type));
170     return descriptor;
171 }
172
173 void iohandler_set_timeout(struct IODescriptor *descriptor, struct timeval *timeout) {
174     if(descriptor->prev)
175         descriptor->prev->next = descriptor->next;
176     else
177         first_descriptor = descriptor->next;
178     if(descriptor->next)
179         descriptor->next->prev = descriptor->prev;
180     if(descriptor == timer_priority)
181         timer_priority = descriptor->next;
182     if(timeout) 
183         descriptor->timeout = *timeout;
184     else {
185         descriptor->timeout.tv_sec = 0;
186         descriptor->timeout.tv_usec = 0;
187     }
188     iohandler_append(descriptor);
189 }
190
191 static void iohandler_increase_iobuf(struct IOBuffer *iobuf, size_t required) {
192     if(iobuf->buflen >= required) return;
193     char *new_buf = realloc(iobuf->buffer, required + 2);
194     if(new_buf) {
195         iobuf->buffer = new_buf;
196         iobuf->buflen = required;
197     }
198 }
199
200 struct IODescriptor *iohandler_timer(struct timeval timeout, iohandler_callback *callback) {
201     struct IODescriptor *descriptor;
202     descriptor = iohandler_add(-1, IOTYPE_TIMER, &timeout, callback);
203     if(!descriptor) {
204         iohandler_log(IOLOG_ERROR, "could not allocate memory for IODescriptor in %s:%d", __FILE__, __LINE__);
205         return NULL;
206     }
207     iohandler_log(IOLOG_DEBUG, "added timer descriptor (sec: %d; usec: %d)", timeout.tv_sec, timeout.tv_usec);
208     return descriptor;
209 }
210
211 struct IODescriptor *iohandler_connect(const char *hostname, unsigned int port, int ssl, const char *bindhost, iohandler_callback *callback) {
212     //non-blocking connect
213     int sockfd;
214     struct addrinfo hints, *res, *freeres;
215     struct sockaddr_in *ip4 = NULL;
216     struct sockaddr_in6 *ip6 = NULL;
217     size_t dstaddrlen;
218     struct sockaddr *dstaddr = NULL;
219     struct IODescriptor *descriptor;
220     
221     memset (&hints, 0, sizeof (hints));
222     hints.ai_family = PF_UNSPEC;
223     hints.ai_socktype = SOCK_STREAM;
224     hints.ai_flags |= AI_CANONNAME;
225     if (getaddrinfo (hostname, NULL, &hints, &res)) {
226         iohandler_log(IOLOG_ERROR, "could not resolve %d to an IP address", hostname);
227         return NULL;
228     }
229     while (res) {
230         switch (res->ai_family) {
231         case AF_INET:
232             ip4 = (struct sockaddr_in *) res->ai_addr;
233             break;
234         case AF_INET6:
235             ip6 = (struct sockaddr_in6 *) res->ai_addr;
236             break;
237         }
238         freeres = res;
239         res = res->ai_next;
240         freeaddrinfo(res);
241     }
242     
243     if(ip6) {
244         sockfd = socket(AF_INET6, SOCK_STREAM, 0);
245         if(sockfd == -1) {
246             iohandler_log(IOLOG_ERROR, "could not create socket in %s:%d", __FILE__, __LINE__);
247             return NULL;
248         }
249         
250         ip6->sin6_family = AF_INET6;
251         ip6->sin6_port = htons(port);
252         
253         struct sockaddr_in6 *ip6vhost = NULL;
254         if (bindhost && !getaddrinfo(bindhost, NULL, &hints, &res)) {
255             while (res) {
256                 switch (res->ai_family) {
257                 case AF_INET6:
258                     ip6vhost = (struct sockaddr_in6 *) res->ai_addr;
259                     break;
260                 }
261                 freeres = res;
262                 res = res->ai_next;
263                 freeaddrinfo(res);
264             }
265         }
266         if(ip6vhost) {
267             ip6vhost->sin6_family = AF_INET6;
268             ip6vhost->sin6_port = htons(0);
269             bind(sockfd, (struct sockaddr*)ip6vhost, sizeof(*ip6vhost));
270         }
271         dstaddr = (struct sockaddr*)ip6;
272         dstaddrlen = sizeof(*ip6);
273     } else if(ip4) {
274         sockfd = socket(AF_INET, SOCK_STREAM, 0);
275         if(sockfd == -1) {
276             iohandler_log(IOLOG_ERROR, "could not create socket in %s:%d", __FILE__, __LINE__);
277             return NULL;
278         }
279         
280         ip4->sin_family = AF_INET;
281         ip4->sin_port = htons(port);
282         
283         struct sockaddr_in *ip4vhost = NULL;
284         if (bindhost && !getaddrinfo(bindhost, NULL, &hints, &res)) {
285             while (res) {
286                 switch (res->ai_family) {
287                 case AF_INET:
288                     ip4vhost = (struct sockaddr_in *) res->ai_addr;
289                     break;
290                 }
291                 freeres = res;
292                 res = res->ai_next;
293                 freeaddrinfo(res);
294             }
295         }
296         if(ip4vhost) {
297             ip4vhost->sin_family = AF_INET;
298             ip4vhost->sin_port = htons(0);
299             bind(sockfd, (struct sockaddr*)ip4vhost, sizeof(*ip4vhost));
300         }
301         dstaddr = (struct sockaddr*)ip4;
302         dstaddrlen = sizeof(*ip4);
303     } else
304         return NULL;
305     //make sockfd unblocking
306 #if defined(F_GETFL)
307     {
308         int flags;
309         flags = fcntl(sockfd, F_GETFL);
310         fcntl(sockfd, F_SETFL, flags|O_NONBLOCK);
311         flags = fcntl(sockfd, F_GETFD);
312         fcntl(sockfd, F_SETFD, flags|FD_CLOEXEC);
313     }
314 #else
315     /* I hope you're using the Win32 backend or something else that
316      * automatically marks the file descriptor non-blocking...
317      */
318 #endif
319     descriptor = iohandler_add(sockfd, IOTYPE_CLIENT, NULL, callback);
320     if(!descriptor) {
321         close(sockfd);
322         return NULL;
323     }
324     connect(sockfd, dstaddr, dstaddrlen); //returns EINPROGRESS here (nonblocking)
325     descriptor->state = IO_CONNECTING;
326     descriptor->read_lines = 1;
327     engine->update(descriptor);
328     iohandler_log(IOLOG_DEBUG, "added client socket (%d) connecting to %s:%d", sockfd, hostname, port);
329     return descriptor;
330 }
331
332 struct IODescriptor *iohandler_listen(const char *hostname, unsigned int port, int ssl, iohandler_callback *callback) {
333     int sockfd;
334     struct addrinfo hints, *res, *freeres;
335     struct sockaddr_in *ip4 = NULL;
336     struct sockaddr_in6 *ip6 = NULL;
337     struct IODescriptor *descriptor;
338     unsigned int opt;
339     
340     memset (&hints, 0, sizeof (hints));
341     hints.ai_family = PF_UNSPEC;
342     hints.ai_socktype = SOCK_STREAM;
343     hints.ai_flags |= AI_CANONNAME;
344     if (getaddrinfo (hostname, NULL, &hints, &res)) {
345         return NULL;
346     }
347     while (res) {
348         switch (res->ai_family) {
349         case AF_INET:
350             ip4 = (struct sockaddr_in *) res->ai_addr;
351             break;
352         case AF_INET6:
353             ip6 = (struct sockaddr_in6 *) res->ai_addr;
354             break;
355         }
356         freeres = res;
357         res = res->ai_next;
358         freeaddrinfo(res);
359     }
360     
361     if(ip6) {
362         sockfd = socket(AF_INET6, SOCK_STREAM, 0);
363         if(sockfd == -1) return NULL;
364         
365         opt = 1;
366         setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt));
367         
368         ip6->sin6_family = AF_INET6;
369         ip6->sin6_port = htons(port);
370         
371         bind(sockfd, (struct sockaddr*)ip6, sizeof(*ip6));
372     } else if(ip4) {
373         sockfd = socket(AF_INET, SOCK_STREAM, 0);
374         if(sockfd == -1) return NULL;
375         
376         opt = 1;
377         setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(opt));
378         
379         ip4->sin_family = AF_INET;
380         ip4->sin_port = htons(port);
381         
382         bind(sockfd, (struct sockaddr*)ip4, sizeof(*ip4));
383     } else
384         return NULL;
385     //make sockfd unblocking
386 #if defined(F_GETFL)
387     {
388         int flags;
389         flags = fcntl(sockfd, F_GETFL);
390         fcntl(sockfd, F_SETFL, flags|O_NONBLOCK);
391         flags = fcntl(sockfd, F_GETFD);
392         fcntl(sockfd, F_SETFD, flags|FD_CLOEXEC);
393     }
394 #else
395     /* I hope you're using the Win32 backend or something else that
396      * automatically marks the file descriptor non-blocking...
397      */
398 #endif
399     descriptor = iohandler_add(sockfd, IOTYPE_SERVER, NULL, callback);
400     if(!descriptor) {
401         close(sockfd);
402         return NULL;
403     }
404     listen(sockfd, 1);
405     descriptor->state = IO_LISTENING;
406     engine->update(descriptor);
407     iohandler_log(IOLOG_DEBUG, "added server socket (%d) listening on %s:%d", sockfd, hostname, port);
408     return descriptor;
409 }
410
411 void iohandler_write(struct IODescriptor *iofd, const char *line) {
412     size_t linelen = strlen(line);
413     iohandler_send(iofd, line, linelen);
414 }
415
416 void iohandler_send(struct IODescriptor *iofd, const char *data, size_t datalen) {
417     if(iofd->type == IOTYPE_TIMER || iofd->state == IO_CLOSED) {
418         iohandler_log(IOLOG_ERROR, "could not write to socket (%s)", (iofd->type == IOTYPE_TIMER ? "IOTYPE_TIMER" : "IO_CLOSED"));
419         return;
420     }
421     iohandler_log(IOLOG_DEBUG, "add %d to writebuf (fd: %d): %s", datalen, iofd->fd, data);
422     if(iofd->writebuf.buflen < iofd->writebuf.bufpos + datalen) {
423         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));
424         iohandler_increase_iobuf(&iofd->writebuf, iofd->writebuf.bufpos + datalen);
425         if(iofd->writebuf.buflen < iofd->writebuf.bufpos + datalen) {
426             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));
427             return;
428         }
429     }
430     memcpy(iofd->writebuf.buffer + iofd->writebuf.bufpos, data, datalen);
431     iofd->writebuf.bufpos += datalen;
432     engine->update(iofd);
433 }
434
435 void iohandler_printf(struct IODescriptor *iofd, const char *text, ...) {
436     va_list arg_list;
437     char sendBuf[LINELEN];
438     int pos;
439     sendBuf[0] = '\0';
440     va_start(arg_list, text);
441     pos = vsnprintf(sendBuf, LINELEN - 2, text, arg_list);
442     va_end(arg_list);
443     if (pos < 0 || pos > (LINELEN - 2)) pos = LINELEN - 2;
444     sendBuf[pos] = '\n';
445     sendBuf[pos+1] = '\0';
446     iohandler_send(iofd, sendBuf, pos+1);
447 }
448
449 void iohandler_try_write(struct IODescriptor *iofd) {
450     if(!iofd->writebuf.bufpos) return;
451     iohandler_log(IOLOG_DEBUG, "write writebuf (%d bytes) to socket (fd: %d)", iofd->writebuf.bufpos, iofd->fd);
452     int res = send(iofd->fd, iofd->writebuf.buffer, iofd->writebuf.bufpos, 0);
453     if(res < 0) {
454         if (errno != EAGAIN) {
455             iohandler_log(IOLOG_ERROR, "could not write to socket (fd: %d): %d - %s", iofd->fd, errno, strerror(errno));
456         }
457     } else {
458         iofd->writebuf.bufpos -= res;
459         if(iofd->state != IO_CLOSED)
460             engine->update(iofd);
461     }
462 }
463
464 void iohandler_close(struct IODescriptor *iofd) {
465     int engine_remove = 1;
466     iofd->state = IO_CLOSED;
467     if(iofd->writebuf.bufpos) {
468         //try to send everything before closing
469 #if defined(F_GETFL)
470         {
471             int flags;
472             flags = fcntl(iofd->fd, F_GETFL);
473             fcntl(iofd->fd, F_SETFL, flags & ~O_NONBLOCK);
474             flags = fcntl(iofd->fd, F_GETFD);
475             fcntl(iofd->fd, F_SETFD, flags|FD_CLOEXEC);
476         }
477 #else
478         engine_remove = 0;
479         engine->remove(iofd);
480 #endif
481         iohandler_try_write(iofd);
482     }
483     //close IODescriptor
484     if(iofd->type == IOTYPE_SERVER || iofd->type == IOTYPE_CLIENT || iofd->type == IOTYPE_STDIN)
485         close(iofd->fd);
486     iohandler_remove(iofd, engine_remove);
487 }
488
489 void iohandler_update(struct IODescriptor *iofd) {
490     iohandler_log(IOLOG_DEBUG, "external call to iohandler_update (fd: %d)", iofd->fd);
491     engine->update(iofd);
492 }
493
494 static void iohandler_trigger_event(struct IOEvent *event) {
495     if(!event->iofd->callback) return;
496     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);
497     event->iofd->callback(event);
498 }
499
500 void iohandler_events(struct IODescriptor *iofd, int readable, int writeable) {
501     struct IOEvent callback_event;
502     callback_event.type = IOEVENT_IGNORE;
503     callback_event.iofd = iofd;
504     switch(iofd->state) {
505         case IO_CLOSED:
506             if(iofd->type == IOTYPE_TIMER)
507                 callback_event.type = IOEVENT_TIMEOUT;
508             break;
509         case IO_LISTENING:
510             if(readable) {
511                 callback_event.data.accept_fd = accept(iofd->fd, NULL, 0);
512                 if(callback_event.data.accept_fd < 0) {
513                     iohandler_log(IOLOG_ERROR, "could not accept client (server fd: %d): %d - %s", iofd->fd, errno, strerror(errno));
514                 } else
515                     callback_event.type = IOEVENT_ACCEPT;
516             }
517             break;
518         case IO_CONNECTING:
519             if(readable) { //could not connect
520                 callback_event.type = IOEVENT_NOTCONNECTED;
521                 socklen_t arglen;
522                 arglen = sizeof(callback_event.data.errid);
523                 if (getsockopt(iofd->fd, SOL_SOCKET, SO_ERROR, &callback_event.data.errid, &arglen) < 0)
524                     callback_event.data.errid = errno;
525                 iofd->state = IO_CLOSED;
526                                 engine->update(iofd);
527             } else if(writeable) {
528                 callback_event.type = IOEVENT_CONNECTED;
529                 iofd->state = IO_CONNECTED;
530                 engine->update(iofd);
531             }
532             break;
533         case IO_CONNECTED:
534             if(readable) {
535                 if(iofd->read_lines) {
536                     int bytes = recv(iofd->fd, iofd->readbuf.buffer + iofd->readbuf.bufpos, iofd->readbuf.buflen - iofd->readbuf.bufpos, 0);
537                     if(bytes <= 0) {
538                         if (errno != EAGAIN) {
539                             iofd->state = IO_CLOSED;
540                                                         engine->update(iofd);
541                             callback_event.type = IOEVENT_CLOSED;
542                             callback_event.data.errid = errno;
543                         }
544                     } else {
545                         int i, used_bytes = 0;
546                         iohandler_log(IOLOG_DEBUG, "received %d bytes (fd: %d). readbuf position: %d", bytes, iofd->fd, iofd->readbuf.bufpos);
547                         iofd->readbuf.bufpos += bytes;
548                         callback_event.type = IOEVENT_RECV;
549                         for(i = 0; i < iofd->readbuf.bufpos; i++) {
550                             if(iofd->readbuf.buffer[i] == '\r' && iofd->readbuf.buffer[i+1] == '\n')
551                                 iofd->readbuf.buffer[i] = 0;
552                             else if(iofd->readbuf.buffer[i] == '\n' || iofd->readbuf.buffer[i] == '\r') {
553                                 iofd->readbuf.buffer[i] = 0;
554                                 callback_event.data.recv_str = iofd->readbuf.buffer + used_bytes;
555                                 iohandler_log(IOLOG_DEBUG, "parsed line (%d bytes): %s", i - used_bytes, iofd->readbuf.buffer + used_bytes);
556                                 used_bytes = i+1;
557                                 iohandler_trigger_event(&callback_event);
558                             } else if(i + 1 - used_bytes >= LINELEN) { //512 max
559                                 iofd->readbuf.buffer[i] = 0;
560                                 callback_event.data.recv_str = iofd->readbuf.buffer + used_bytes;
561                                 iohandler_log(IOLOG_DEBUG, "parsed and stripped line (%d bytes): %s", i - used_bytes, iofd->readbuf.buffer + used_bytes);
562                                 for(; i < iofd->readbuf.bufpos; i++) { //skip the rest of the line
563                                     if(iofd->readbuf.buffer[i] == '\n' || (iofd->readbuf.buffer[i] == '\r' && iofd->readbuf.buffer[i+1] != '\n')) {
564                                         break;
565                                     }
566                                 }
567                                 used_bytes = i+1;
568                                 iohandler_trigger_event(&callback_event);
569                             }
570                         }
571                         if(used_bytes) {
572                             if(used_bytes == iofd->readbuf.bufpos) {
573                                 iofd->readbuf.bufpos = 0;
574                                 iohandler_log(IOLOG_DEBUG, "readbuf fully processed (set buffer position to 0)");
575                             } else {
576                                 iohandler_log(IOLOG_DEBUG, "readbuf rest: %d bytes (used %d bytes)", iofd->readbuf.bufpos - used_bytes, used_bytes);
577                                 memmove(iofd->readbuf.buffer, iofd->readbuf.buffer + used_bytes, iofd->readbuf.bufpos - used_bytes);
578                                 iofd->readbuf.bufpos -= used_bytes;
579                             }
580                         }
581                         callback_event.type = IOEVENT_IGNORE;
582                     }
583                 } else
584                     callback_event.type = IOEVENT_READABLE;
585             }
586             if(writeable) {
587                 iohandler_try_write(iofd);
588             }
589             break;
590     }
591     if(callback_event.type == IOEVENT_IGNORE && !readable && !writeable) 
592         callback_event.type = IOEVENT_TIMEOUT;
593     if(callback_event.type != IOEVENT_IGNORE)
594         iohandler_trigger_event(&callback_event);
595 }
596
597 void iohandler_poll() {
598     if(engine) {
599         struct timeval timeout;
600         timeout.tv_sec = IO_MAX_TIMEOUT;
601         timeout.tv_usec = 0;
602         engine->loop(&timeout);
603     }
604 }
605
606 //debugging functions
607 char *iohandler_iotype_name(enum IOType type) {
608     switch(type) {
609         case IOTYPE_UNKNOWN:
610             return "IOTYPE_UNKNOWN";
611         case IOTYPE_SERVER:
612             return "IOTYPE_SERVER";
613         case IOTYPE_CLIENT:
614             return "IOTYPE_CLIENT";
615         case IOTYPE_STDIN:
616             return "IOTYPE_STDIN";
617         case IOTYPE_TIMER:
618             return "IOTYPE_TIMER";
619         default:
620             return "(UNDEFINED)";
621     }
622 }
623
624 char *iohandler_iostatus_name(enum IOStatus status) {
625     switch(status) {
626         case IO_CLOSED:
627             return "IO_CLOSED";
628         case IO_LISTENING:
629             return "IO_LISTENING";
630         case IO_CONNECTING:
631             return "IO_CONNECTING";
632         case IO_CONNECTED:
633             return "IO_CONNECTED";
634         default:
635             return "(UNDEFINED)";
636     }
637 }
638
639 char *iohandler_ioeventtype_name(enum IOEventType type) {
640     switch(type) {
641         case IOEVENT_IGNORE:
642             return "IOEVENT_IGNORE";
643         case IOEVENT_READABLE:
644             return "IOEVENT_READABLE";
645         case IOEVENT_RECV:
646             return "IOEVENT_RECV";
647         case IOEVENT_CONNECTED:
648             return "IOEVENT_CONNECTED";
649         case IOEVENT_NOTCONNECTED:
650             return "IOEVENT_NOTCONNECTED";
651         case IOEVENT_CLOSED:
652             return "IOEVENT_CLOSED";
653         case IOEVENT_ACCEPT:
654             return "IOEVENT_ACCEPT";
655         case IOEVENT_TIMEOUT:
656             return "IOEVENT_TIMEOUT";
657         default:
658             return "(UNDEFINED)";
659     }
660 }