diff --git a/kike.c b/kike.c index 15c31df..7bbfef4 100644 --- a/kike.c +++ b/kike.c @@ -2760,81 +2760,84 @@ client_update_poller (struct client *c, const struct pollfd *pfd) poller_fd_set (&c->socket_event, new_events); } +static bool +irc_try_fetch_client (struct server_context *ctx, int listen_fd) +{ + // XXX: `struct sockaddr_storage' is not the most portable thing + struct sockaddr_storage peer; + socklen_t peer_len = sizeof peer; + + int fd = accept (listen_fd, (struct sockaddr *) &peer, &peer_len); + if (fd == -1) + { + if (errno == EAGAIN) + return false; + if (errno == EINTR + || errno == ECONNABORTED) + return true; + + // TODO: handle resource exhaustion (EMFILE, ENFILE) specially + // (stop accepting new connections and wait until we close some; + // also set a timer in case of ENFILE). + print_fatal ("%s: %s", "accept", strerror (errno)); + irc_initiate_quit (ctx); + return false; + } + + if (ctx->max_connections != 0 && ctx->n_clients >= ctx->max_connections) + { + print_debug ("connection limit reached, refusing connection"); + close (fd); + return true; + } + + char host[NI_MAXHOST] = "unknown", port[NI_MAXSERV] = "unknown"; + int err = getnameinfo ((struct sockaddr *) &peer, peer_len, + host, sizeof host, port, sizeof port, NI_NUMERICSERV); + if (err) + print_debug ("%s: %s", "getnameinfo", gai_strerror (err)); + + char *address = format_host_port_pair (host, port); + print_debug ("accepted connection from %s", address); + + struct client *c = xmalloc (sizeof *c); + client_init (c); + c->ctx = ctx; + c->socket_fd = fd; + c->hostname = xstrdup (host); + c->address = address; + c->last_active = time (NULL); + LIST_PREPEND (ctx->clients, c); + ctx->n_clients++; + + poller_fd_init (&c->socket_event, &c->ctx->poller, c->socket_fd); + c->socket_event.dispatcher = (poller_fd_fn) on_client_ready; + c->socket_event.user_data = c; + + poller_timer_init (&c->kill_timer, &c->ctx->poller); + c->kill_timer.dispatcher = on_client_kill_timer; + c->kill_timer.user_data = c; + + poller_timer_init (&c->timeout_timer, &c->ctx->poller); + c->timeout_timer.dispatcher = on_client_timeout_timer; + c->timeout_timer.user_data = c; + + poller_timer_init (&c->ping_timer, &c->ctx->poller); + c->ping_timer.dispatcher = on_client_ping_timer; + c->ping_timer.user_data = c; + + set_blocking (fd, false); + client_update_poller (c, NULL); + client_set_kill_timer (c); + return true; +} + static void on_irc_client_available (const struct pollfd *pfd, void *user_data) { - (void) pfd; struct server_context *ctx = user_data; - - while (true) - { - // XXX: `struct sockaddr_storage' is not the most portable thing - struct sockaddr_storage peer; - socklen_t peer_len = sizeof peer; - - int fd = accept (pfd->fd, (struct sockaddr *) &peer, &peer_len); - if (fd == -1) - { - if (errno == EAGAIN) - break; - if (errno == EINTR - || errno == ECONNABORTED) - continue; - - // TODO: handle resource exhaustion (EMFILE, ENFILE) specially - // (stop accepting new connections and wait until we close some; - // also set a timer in case of ENFILE). - print_fatal ("%s: %s", "accept", strerror (errno)); - irc_initiate_quit (ctx); - break; - } - - if (ctx->max_connections != 0 && ctx->n_clients >= ctx->max_connections) - { - print_debug ("connection limit reached, refusing connection"); - close (fd); - continue; - } - - char host[NI_MAXHOST] = "unknown", port[NI_MAXSERV] = "unknown"; - int err = getnameinfo ((struct sockaddr *) &peer, peer_len, - host, sizeof host, port, sizeof port, NI_NUMERICSERV); - if (err) - print_debug ("%s: %s", "getnameinfo", gai_strerror (err)); - - char *address = format_host_port_pair (host, port); - print_debug ("accepted connection from %s", address); - - struct client *c = xmalloc (sizeof *c); - client_init (c); - c->ctx = ctx; - c->socket_fd = fd; - c->hostname = xstrdup (host); - c->address = address; - c->last_active = time (NULL); - LIST_PREPEND (ctx->clients, c); - ctx->n_clients++; - - poller_fd_init (&c->socket_event, &c->ctx->poller, c->socket_fd); - c->socket_event.dispatcher = (poller_fd_fn) on_client_ready; - c->socket_event.user_data = c; - - poller_timer_init (&c->kill_timer, &c->ctx->poller); - c->kill_timer.dispatcher = on_client_kill_timer; - c->kill_timer.user_data = c; - - poller_timer_init (&c->timeout_timer, &c->ctx->poller); - c->timeout_timer.dispatcher = on_client_timeout_timer; - c->timeout_timer.user_data = c; - - poller_timer_init (&c->ping_timer, &c->ctx->poller); - c->ping_timer.dispatcher = on_client_ping_timer; - c->ping_timer.user_data = c; - - set_blocking (fd, false); - client_update_poller (c, NULL); - client_set_kill_timer (c); - } + while (irc_try_fetch_client (ctx, pfd->fd)) + ; } // --- Application setup -------------------------------------------------------