[IOMultiplexer] added auto-reloading timers
[NeonServV5.git] / src / IOHandler.c
index 81d8d0982fd70c9d8db824427baed4acf7fba361..b2a391bf11e68388112f107a56747f84f6e8cde2 100644 (file)
@@ -73,20 +73,30 @@ extern struct IOEngine engine_kevent;
 extern struct IOEngine engine_epoll;
 extern struct IOEngine engine_win32;
 
+int iohandler_settings = 0;
 struct IOEngine *engine = NULL;
 
+void iohandler_set(int setting, int value) {
+    if(value)
+        iohandler_settings |= setting;
+    else
+        iohandler_settings &= ~setting;
+}
+
 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(!(iohandler_settings & IOHANDLER_SETTING_HIGH_PRECISION_TIMER)) {
+        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())
@@ -185,8 +195,13 @@ struct IODescriptor *iohandler_add(int sockfd, enum IOType type, struct timeval
     descriptor->type = type;
     descriptor->state = (type == IOTYPE_STDIN ? IO_CONNECTED : IO_CLOSED);
     descriptor->callback = callback;
-    if(timeout)
+    if(timeout) {
         descriptor->timeout = *timeout;
+        if(descriptor->timeout.tv_usec > 1000000) {
+            descriptor->timeout.tv_usec -= 1000000;
+            descriptor->timeout.tv_sec++;
+        }
+    }
     if(type != IOTYPE_TIMER) {
         descriptor->readbuf.buffer = malloc(IO_READ_BUFLEN + 2);
         descriptor->readbuf.bufpos = 0;
@@ -220,9 +235,20 @@ void iohandler_set_timeout(struct IODescriptor *descriptor, struct timeval *time
         descriptor->next->prev = descriptor->prev;
     if(descriptor == timer_priority)
         timer_priority = descriptor->next;
-    if(timeout) 
+    if(timeout && timeout->tv_sec == 0 && descriptor->constant_timeout) {
+        descriptor->timeout.tv_usec += (descriptor->constant_timeout % 1000) * 1000;
+        descriptor->timeout.tv_sec += (descriptor->constant_timeout / 1000);
+        if(descriptor->timeout.tv_usec > 1000000) {
+            descriptor->timeout.tv_sec += (descriptor->timeout.tv_usec / 1000000);
+            descriptor->timeout.tv_usec %= 1000000;
+        }
+    } else if(timeout) {
         descriptor->timeout = *timeout;
-    else {
+        if(descriptor->timeout.tv_usec > 1000000) {
+            descriptor->timeout.tv_usec -= 1000000;
+            descriptor->timeout.tv_sec++;
+        }
+    } else {
         descriptor->timeout.tv_sec = 0;
         descriptor->timeout.tv_usec = 0;
     }
@@ -249,7 +275,31 @@ struct IODescriptor *iohandler_timer(struct timeval timeout, iohandler_callback
     return descriptor;
 }
 
+struct IODescriptor *iohandler_constant_timer(int msec, iohandler_callback *callback) {
+    struct IODescriptor *descriptor;
+    struct timeval timeout;
+    gettimeofday(&timeout, NULL);
+    timeout.tv_usec += (msec % 1000) * 1000;
+    timeout.tv_sec += (msec / 1000);
+    if(timeout.tv_usec > 1000000) {
+        timeout.tv_sec += (timeout.tv_usec / 1000000);
+        timeout.tv_usec %= 1000000;
+    }
+    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;
+    }
+    descriptor->constant_timeout = msec;
+    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) {
+    return iohandler_connect_flags(hostname, port, ssl, bindhost, callback, IOHANDLER_CONNECT_IPV4 | IOHANDLER_CONNECT_IPV6);
+}
+
+struct IODescriptor *iohandler_connect_flags(const char *hostname, unsigned int port, int ssl, const char *bindhost, iohandler_callback *callback, int flags) {
     //non-blocking connect
     int sockfd, result;
     struct addrinfo hints, *res;
@@ -284,7 +334,7 @@ struct IODescriptor *iohandler_connect(const char *hostname, unsigned int port,
         freeaddrinfo(res);
     }
     
-    if(ip6) {
+    if(ip6 && (flags & IOHANDLER_CONNECT_IPV6)) {
         sockfd = socket(AF_INET6, SOCK_STREAM, 0);
         if(sockfd == -1) {
             iohandler_log(IOLOG_ERROR, "could not create socket in %s:%d", __FILE__, __LINE__);
@@ -313,7 +363,7 @@ struct IODescriptor *iohandler_connect(const char *hostname, unsigned int port,
         }
         dstaddr = (struct sockaddr*)ip6;
         dstaddrlen = sizeof(*ip6);
-    } else if(ip4) {
+    } else if(ip4 && (flags & IOHANDLER_CONNECT_IPV4)) {
         sockfd = socket(AF_INET, SOCK_STREAM, 0);
         if(sockfd == -1) {
             iohandler_log(IOLOG_ERROR, "could not create socket in %s:%d", __FILE__, __LINE__);
@@ -384,6 +434,10 @@ struct IODescriptor *iohandler_connect(const char *hostname, unsigned int port,
 }
 
 struct IODescriptor *iohandler_listen(const char *hostname, unsigned int port, iohandler_callback *callback) {
+    return iohandler_listen_flags(hostname, port, callback, IOHANDLER_LISTEN_IPV4 | IOHANDLER_LISTEN_IPV6);
+}
+
+struct IODescriptor *iohandler_listen_flags(const char *hostname, unsigned int port, iohandler_callback *callback, int flags) {
     int sockfd;
     struct addrinfo hints, *res;
     struct sockaddr_in *ip4 = NULL;
@@ -415,7 +469,7 @@ struct IODescriptor *iohandler_listen(const char *hostname, unsigned int port, i
         freeaddrinfo(res);
     }
     
-    if(ip6) {
+    if(ip6 && (flags & IOHANDLER_LISTEN_IPV6)) {
         sockfd = socket(AF_INET6, SOCK_STREAM, 0);
         if(sockfd == -1) return NULL;
         
@@ -426,7 +480,7 @@ struct IODescriptor *iohandler_listen(const char *hostname, unsigned int port, i
         ip6->sin6_port = htons(port);
         
         bind(sockfd, (struct sockaddr*)ip6, sizeof(*ip6));
-    } else if(ip4) {
+    } else if(ip4 && (flags && IOHANDLER_LISTEN_IPV4)) {
         sockfd = socket(AF_INET, SOCK_STREAM, 0);
         if(sockfd == -1) return NULL;
         
@@ -453,11 +507,11 @@ struct IODescriptor *iohandler_listen(const char *hostname, unsigned int port, i
     //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);
+        int flag;
+        flag = fcntl(sockfd, F_GETFL);
+        fcntl(sockfd, F_SETFL, flag|O_NONBLOCK);
+        flag = fcntl(sockfd, F_GETFD);
+        fcntl(sockfd, F_SETFD, flag|FD_CLOEXEC);
     }
     #else
     /* I hope you're using the Win32 backend or something else that
@@ -476,6 +530,21 @@ struct IODescriptor *iohandler_listen(const char *hostname, unsigned int port, i
     return descriptor;
 }
 
+struct IODescriptor *iohandler_listen_ssl(const char *hostname, unsigned int port, const char *certfile, const char *keyfile, iohandler_callback *callback) {
+    return iohandler_listen_ssl_flags(hostname, port, certfile, keyfile, callback, IOHANDLER_LISTEN_IPV4 | IOHANDLER_LISTEN_IPV6);
+}
+
+struct IODescriptor *iohandler_listen_ssl_flags(const char *hostname, unsigned int port, const char *certfile, const char *keyfile, iohandler_callback *callback, int flags) {
+    struct IODescriptor *descriptor = iohandler_listen_flags(hostname, port, callback, flags);
+    if(!descriptor)
+        return NULL;
+    //SSL Server Socket
+    iohandler_ssl_listen(descriptor, certfile, keyfile);
+    if(descriptor->sslnode)
+        descriptor->ssl = 1;
+    return descriptor;
+}
+
 void iohandler_write(struct IODescriptor *iofd, const char *line) {
     size_t linelen = strlen(line);
     iohandler_send(iofd, line, linelen);
@@ -580,9 +649,15 @@ void iohandler_events(struct IODescriptor *iofd, int readable, int writeable) {
     switch(iofd->state) {
         case IO_SSLWAIT:
             if(!readable && !writeable) {
-                callback_event.type = IOEVENT_SSLFAILED;
-                iofd->state = IO_CLOSED;
-                engine->update(iofd);
+                if(!iofd->ssl_server_hs) {
+                    callback_event.type = IOEVENT_SSLFAILED;
+                    iofd->state = IO_CLOSED;
+                    engine->update(iofd);
+                } else
+                    iohandler_close(iofd);
+            } else if(iofd->ssl_server_hs) {
+                iohandler_log(IOLOG_DEBUG, "triggering iohandler_ssl_server_handshake for %s (fd: %d)", iohandler_iotype_name(iofd->type), iofd->fd);
+                iohandler_ssl_server_handshake(iofd);
             } 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);
@@ -597,6 +672,9 @@ void iohandler_events(struct IODescriptor *iofd, int readable, int writeable) {
                 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 if(iofd->ssl) {
+                    struct IODescriptor *client_iofd = iohandler_add(callback_event.data.accept_fd, IOTYPE_CLIENT, NULL, NULL);
+                    iohandler_ssl_client_accepted(iofd, client_iofd);
                 } else
                     callback_event.type = IOEVENT_ACCEPT;
             }
@@ -616,7 +694,14 @@ void iohandler_events(struct IODescriptor *iofd, int readable, int writeable) {
                     iohandler_ssl_connect(iofd);
                     return;
                 }
-                callback_event.type = IOEVENT_CONNECTED;
+                if(iofd->ssl && iofd->ssl_server_hs) {
+                    callback_event.type = IOEVENT_SSLACCEPT;
+                    callback_event.iofd = iofd->data;
+                    callback_event.data.accept_iofd = iofd;
+                    iofd->data = NULL;
+                }
+                else 
+                    callback_event.type = IOEVENT_CONNECTED;
                 iofd->state = IO_CONNECTED;
                 engine->update(iofd);
             }
@@ -706,11 +791,15 @@ void iohandler_events(struct IODescriptor *iofd, int readable, int writeable) {
 }
 
 void iohandler_poll() {
+    struct timeval timeout;
+    timeout.tv_sec = IO_MAX_TIMEOUT;
+    timeout.tv_usec = 0;
+    iohandler_poll_timeout(timeout);
+}
+
+void iohandler_poll_timeout(struct timeval timeout) {
     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);
     }
@@ -767,6 +856,8 @@ char *iohandler_ioeventtype_name(enum IOEventType type) {
             return "IOEVENT_CLOSED";
         case IOEVENT_ACCEPT:
             return "IOEVENT_ACCEPT";
+        case IOEVENT_SSLACCEPT:
+            return "IOEVENT_SSLACCEPT";
         case IOEVENT_TIMEOUT:
             return "IOEVENT_TIMEOUT";
         default: