From 5c17d27a9582de9ced5a55dc77616ce100b97d3a Mon Sep 17 00:00:00 2001 From: nohar Date: Fri, 5 Jan 2007 21:19:32 +0000 Subject: [PATCH] Re-think who_count management. Should fix hard to trigger bug: fatal("negative who_count"). --- src/irc.c | 141 ++++++++++++++++++++++++++++++++++++++--------------- src/irc.h | 1 + src/util.c | 2 +- 3 files changed, 104 insertions(+), 40 deletions(-) diff --git a/src/irc.c b/src/irc.c index 3afeeec..32d82ae 100644 --- a/src/irc.c +++ b/src/irc.c @@ -396,16 +396,44 @@ static int irc_315(struct link_server *server, struct line *l) { struct link *link = LINK(server); if (link->who_client) { - --link->who_client->who_count; - - if (link->who_client->who_count < 0) - fatal("negative who count"); + if (link->who_client->who_count == 0) { + mylog(LOG_DEBUG, "Spurious irc_315"); + return OK_COPY_WHO; + } + link->who_client->whoc_tstamp = time(NULL); + if (link->who_client->who_count > 0) { + --link->who_client->who_count; + mylog(LOG_INFO, + "RPL_ENDOFWHO: " + "Decrementing who count for %p: %d", + link->who_client, link->who_client->who_count); + } } l = NULL; /* keep gcc happy */ return OK_COPY_WHO; } +void rotate_who_client(struct link *link) +{ + int i; + mylog(LOG_INFO, "rotate_who_client %p", link->who_client); + /* find a client with non-null who_count */ + link->who_client = NULL; + for (i = 0; i < link->l_clientc; i++) { + struct link_client *ic = link->l_clientv[i]; + if (!list_is_empty(&ic->who_queue)) { + char *l; + while ((l = list_remove_first(&ic->who_queue))) { + write_line(CONN(link->l_server), l); + free(l); + } + link->who_client = ic; + break; + } + } +} + /* * parses:join part mode kick kill privmsg quit nick names * returns: -1 invalid protocol @@ -540,32 +568,12 @@ int irc_dispatch_server(struct link_server *server, struct line *line) s = irc_line_to_string(line); write_line(CONN(LINK(server)->who_client), s); free(s); - - if (LINK(server)->who_client->who_count == 0) { - int i; - for (i = 0; i < LINK(server)->l_clientc; i++) { - struct link_client *ic = - LINK(server)->l_clientv[i]; - if (ic == LINK(server)->who_client) { - if (!list_is_empty(&ic->who_queue)) - fatal("who active and queued?"); - continue; - } - - if (!list_is_empty(&ic->who_queue)) { - char *l; - while ((l = list_remove_first( - &ic->who_queue))) { - write_line(CONN(server), l); - free(l); - } - LINK(server)->who_client = ic; - break; - } - } - if (i == LINK(server)->l_clientc) - LINK(server)->who_client = NULL; - } + } + if (LINK(server)->who_client && + LINK(server)->who_client->who_count == 0) { + mylog(LOG_INFO, "OK_COPY_WHO: who_count for %p is nul", + LINK(server)->who_client); + rotate_who_client(LINK(server)); } return ret; } @@ -649,8 +657,16 @@ void unbind_from_link(struct link_client *ic) if (i == l->l_clientc) fatal("unbind_from_link"); - if (l->who_client == ic) + if (l->who_client == ic) { + mylog(LOG_INFO, "unbind_from_link: %p: %d", + l->who_client, ic->who_count); l->who_client = NULL; + } else { + mylog(LOG_INFO, + "unbind_from_link: nothing to do %p != %p: %d", + ic, l->who_client, + ic->who_count); + } for (i = i + 1; i < l->l_clientc; i++) l->l_clientv[i - 1] = l->l_clientv[i]; @@ -948,6 +964,12 @@ static int irc_cli_who(struct link_client *ic, struct line *line) { struct link *l = LINK(ic); + ++ic->who_count; + if (ic->who_count == 1) + ic->whoc_tstamp = time(NULL); + mylog(LOG_INFO, "cli_who: Incrementing who count for %p: %d", + ic, ic->who_count); + if (l->who_client && l->who_client != ic) { list_add_first(&ic->who_queue, irc_line_to_string(line)); return OK_FORGET; @@ -956,8 +978,6 @@ static int irc_cli_who(struct link_client *ic, struct line *line) if (!l->who_client) l->who_client = ic; - ++ic->who_count; - return OK_COPY; } @@ -973,6 +993,12 @@ static int irc_cli_mode(struct link_client *ic, struct line *line) strchr(line->elemv[2], 'b') == NULL) return OK_COPY; + ++ic->who_count; + if (ic->who_count == 1) + ic->whoc_tstamp = time(NULL); + mylog(LOG_INFO, "cli_mode: Incrementing who count for %p: %d", + l->who_client, ic->who_count); + if (l->who_client && l->who_client != ic) { list_add_first(&ic->who_queue, irc_line_to_string(line)); return OK_FORGET; @@ -981,8 +1007,6 @@ static int irc_cli_mode(struct link_client *ic, struct line *line) if (!l->who_client) l->who_client = ic; - ++ic->who_count; - return OK_COPY; } @@ -1486,10 +1510,19 @@ static int irc_368(struct link_server *server, struct line *l) { struct link *link = LINK(server); if (link->who_client) { - --link->who_client->who_count; + if (link->who_client->who_count == 0) { + mylog(LOG_DEBUG, "Spurious irc_368"); + return OK_COPY_WHO; + } + link->who_client->whoc_tstamp = time(NULL); - if (link->who_client->who_count < 0) - fatal("negative who count"); + if (link->who_client->who_count > 0) { + --link->who_client->who_count; + mylog(LOG_INFO, + "RPL_ENDOFBANLIST: " + "Decrementing who count for %p: %d", + link->who_client, link->who_client->who_count); + } } l = NULL; /* keep gcc happy */ @@ -2202,6 +2235,29 @@ void oidentd_dump(list_t *connl) /* do not use */ list_t *_connections = NULL; +void timeout_clean_who_counts(list_t *conns) +{ + list_iterator_t it; + for (list_it_init(conns, &it); list_it_item(&it); list_it_next(&it)) { + struct link *l = list_it_item(&it); + struct link_client *client = l->who_client; + + if (client && client->whoc_tstamp) { + time_t now; + now = time(NULL); + if (now - client->whoc_tstamp > 10) { + mylog(LOG_DEBUG, "Yawn, " + "forgetting one who reply"); + if (client->who_count > 0) + --client->who_count; + client->whoc_tstamp = time(NULL); + if (client->who_count == 0) + rotate_who_client(l); + } + } + } +} + struct link_client *reloading_client; /* * The main loop @@ -2268,8 +2324,10 @@ void irc_main(connection_t *inc, list_t *ll) struct link *link; connection_t *conn; - /* Compute timeouts for next reconnections and lagouts */ if (timeleft == 0) { + /* + * Compute timeouts for next reconnections and lagouts + */ static int throttle_prot = S_CONN_DELAY - 1; timeleft = 1000; @@ -2328,6 +2386,11 @@ void irc_main(connection_t *inc, list_t *ll) list_it_remove(&li); } } + + /* + * Cleanup lagging or dangling who_count buffers + */ + timeout_clean_who_counts(ll); } int nc; diff --git a/src/irc.h b/src/irc.h index 746401d..8f2531d 100644 --- a/src/irc.h +++ b/src/irc.h @@ -155,6 +155,7 @@ struct link_client { list_t who_queue; int who_count; + time_t whoc_tstamp; #ifdef HAVE_LIBSSL int allow_trust; diff --git a/src/util.c b/src/util.c index 4e1c479..91b86a4 100644 --- a/src/util.c +++ b/src/util.c @@ -459,7 +459,7 @@ static unsigned char hash_func(char *pkey) char c; unsigned long hash = 5381; /* 5381 & 0xff makes more sense */ - while (c = *pkey++) + while ((c = *pkey++)) hash = ((hash << 5) + hash) ^ c; return (unsigned char)hash; }