diff --git a/kike.c b/kike.c index 602f654..9d3878b 100644 --- a/kike.c +++ b/kike.c @@ -388,7 +388,8 @@ client_free (struct client *self) str_map_free (&self->invites); } -static void client_close_link (struct client *, const char *); +static void client_close_link (struct client *c, const char *reason); +static void client_kill (struct client *c, const char *reason); static void client_send (struct client *, const char *, ...) ATTRIBUTE_PRINTF (2, 3); static void client_cancel_timers (struct client *); @@ -623,8 +624,13 @@ static void on_irc_quit_timeout (void *user_data) { struct server_context *ctx = user_data; - // Clients are closed in server_context_free() - ctx->polling = false; + struct client *iter, *next; + for (iter = ctx->clients; iter; iter = next) + { + next = iter->next; + // irc_initiate_quit() has already unregistered the client + client_kill (iter, "Shutting down"); + } } static void @@ -676,19 +682,9 @@ server_context_free (struct server_context *self) free (self->listen_fds); free (self->listen_events); + hard_assert (!self->clients); if (self->ssl_ctx) SSL_CTX_free (self->ssl_ctx); - struct client *link, *tmp; - for (link = self->clients; link; link = tmp) - { - tmp = link->next; - // FIXME: either make sure this isn't called (which it can as of now, - // see on_irc_quit_timeout) or fix client_free() to unregister from - // the poller before closing the socket (we could also just - // set .closed = true). - client_free (link); - free (link); - } free (self->server_name); str_map_free (&self->users); @@ -729,10 +725,6 @@ irc_initiate_quit (struct server_context *ctx) { print_status ("shutting down"); - for (struct client *iter = ctx->clients; iter; iter = iter->next) - if (!iter->closing_link) - client_close_link (iter, "Shutting down"); - for (size_t i = 0; i < ctx->n_listen_fds; i++) { xclose (ctx->listen_fds[i]); @@ -741,6 +733,10 @@ irc_initiate_quit (struct server_context *ctx) } ctx->n_listen_fds = 0; + for (struct client *iter = ctx->clients; iter; iter = iter->next) + if (!iter->closing_link) + client_close_link (iter, "Shutting down"); + ctx->quitting = true; poller_timer_set (&ctx->quit_timer, 5000); irc_try_finish_quit (ctx); @@ -888,6 +884,25 @@ client_unregister (struct client *c, const char *reason) c->registered = false; } +static void +client_close_link (struct client *c, const char *reason) +{ + if (!soft_assert (!c->closing_link)) + return; + + // We push an `ERROR' message to the write buffer and let the poller send + // it, with some arbitrary timeout. The `closing_link' state makes sure + // that a/ we ignore any successive messages, and b/ that the connection + // is killed after the write buffer is transferred and emptied. + client_send (c, "ERROR :Closing Link: %s[%s] (%s)", + c->nickname ? c->nickname : "*", + c->hostname /* TODO host IP? */, reason); + c->closing_link = true; + + client_unregister (c, reason); + client_set_kill_timer (c); +} + static void client_kill (struct client *c, const char *reason) { @@ -917,25 +932,6 @@ client_kill (struct client *c, const char *reason) irc_try_finish_quit (ctx); } -static void -client_close_link (struct client *c, const char *reason) -{ - if (!soft_assert (!c->closing_link)) - return; - - // We push an `ERROR' message to the write buffer and let the poller send - // it, with some arbitrary timeout. The `closing_link' state makes sure - // that a/ we ignore any successive messages, and b/ that the connection - // is killed after the write buffer is transferred and emptied. - client_send (c, "ERROR :Closing Link: %s[%s] (%s)", - c->nickname ? c->nickname : "*", - c->hostname /* TODO host IP? */, reason); - c->closing_link = true; - - client_unregister (c, reason); - client_set_kill_timer (c); -} - static bool client_in_mask_list (const struct client *c, const struct str_vector *mask) {