X-Git-Url: http://git.pk910.de/?a=blobdiff_plain;f=ircd%2Fwhowas.c;h=fed3fffe748325630e016e8aa9efa6971357ac80;hb=refs%2Fheads%2Fupstream-ssl;hp=c2caf4a9d4b53b8a0c6691c37836f3d60bcd3e05;hpb=263c649848b2b25a097134227a7b89d868cb2e1e;p=ircu2.10.12-pk.git diff --git a/ircd/whowas.c b/ircd/whowas.c index c2caf4a..fed3fff 100644 --- a/ircd/whowas.c +++ b/ircd/whowas.c @@ -31,43 +31,56 @@ * * --- Run --- 27th August 1997 * Speeded up the code, added comments. - * - * $Id$ */ +#include "config.h" + #include "whowas.h" #include "client.h" #include "ircd.h" #include "ircd_alloc.h" #include "ircd_chattr.h" +#include "ircd_features.h" +#include "ircd_log.h" #include "ircd_string.h" #include "list.h" #include "numeric.h" +#include "s_debug.h" #include "s_misc.h" #include "s_user.h" #include "send.h" #include "struct.h" -#include "support.h" #include "sys.h" #include "msg.h" -#include +/* #include -- Now using assert in ircd_log.h */ #include +#include -static struct Whowas whowas[NICKNAMEHISTORYLENGTH]; -static struct Whowas* whowas_next = whowas; +/** Keeps track of whowas least-recently-used list. */ +static struct { + struct Whowas *ww_list; /**< list of whowas structures */ + struct Whowas *ww_tail; /**< tail of list for getting structures */ + unsigned int ww_alloc; /**< alloc count */ +} wwList = { 0, 0, 0 }; + +/** Hash table of Whowas entries by nickname. */ struct Whowas* whowashash[WW_MAX]; -/* +/** @file + * @brief Manipulation functions for the whowas list. + * @version $Id$ + * * Since the introduction of numeric nicks (at least for upstream messages, - * like MODE +o , KICK #chan , KILL etc), there is no + * like MODE +o <nick>, KICK #chan <nick>, KILL <nick> etc), there is no * real important reason for a nick history anymore. * Nevertheless, there are two reason why we might want to keep it: - * 1) The /WHOWAS command, which is often usefull to catch harrashing + * @li The /WHOWAS command, which is often useful to catch harassing * users or abusers in general. - * 2) Clients still use the normal nicks in the client-server protocol, + * @li Clients still use the normal nicks in the client-server protocol, * and it might be considered a nice feature that here we still have * nick chasing. + * * Note however that BOTH reasons make it redundant to keep a whowas history * for users that split off. * @@ -90,35 +103,35 @@ struct Whowas* whowashash[WW_MAX]; * is not anymore maintained (and hopefully not used anymore either ;). * * So now we have two ways of accessing this database: - * 1) Given a we can calculate a hashv and then whowashash[hashv] will + * @li Given a <nick> we can calculate a hashv and then whowashash[hashv] will * point to the start of the 'hash list': all entries with the same hashv. - * We'll have to search this list to find the entry with the correct . + * We'll have to search this list to find the entry with the correct <nick>. * Once we found the correct whowas entry, we have a pointer to the - * corresponding client - if still online - for nich chasing purposes. + * corresponding client - if still online - for nick chasing purposes. * Note that the same nick can occur multiple times in the whowas history, * each of these having the same hash value of course. While a /WHOWAS on * just a nick will return all entries, nick chasing will only find the * first in the list. Because new entries are added at the start of the * 'hash list' we will always find the youngest entry, which is what we want. - * 2) Given an online client we have a pointer to the first whowas entry + * @li Given an online client we have a pointer to the first whowas entry * of the linked list of whowas entries that all belong to this client. * We ONLY need this to reset all `online' pointers when this client * signs off. * - * 27/8/79: + * 27/8/97: * * Note that following: * - * a) We *only* (need to) change the 'hash list' and the 'online' list + * @li We *only* (need to) change the 'hash list' and the 'online' list * in add_history(). - * b) There we always ADD an entry to the BEGINNING of the 'hash list' + * @li There we always ADD an entry to the BEGINNING of the 'hash list' * and the 'online list': *new* entries are at the start of the lists. * The oldest entries are at the end of the lists. - * c) We always REMOVE the oldest entry we have (whowas_next), this means + * @li We always REMOVE the oldest entry we have (whowas_next), this means * that this is always an entry that is at the *end* of the 'hash list' * and 'online list' that it is a part of: the next pointer will * always be NULL. - * d) The previous pointer is *only* used to update the next pointer of the + * @li The previous pointer is *only* used to update the next pointer of the * previous entry, therefore we could better use a pointer to this * next pointer: That is faster - saves us a 'if' test (it will never be * NULL because the last added entry will point to the pointer that @@ -130,118 +143,182 @@ struct Whowas* whowashash[WW_MAX]; * --Run */ -typedef union { - struct Whowas *newww; - struct Whowas *oldww; -} Current; +/** Unlink a Whowas structure and free everything inside it. + * @param[in,out] ww The whowas record to free. + * @return The pointer \a ww. + */ +static struct Whowas * +whowas_clean(struct Whowas *ww) +{ + if (!ww) + return 0; + + Debug((DEBUG_LIST, "Cleaning whowas structure for %s", ww->name)); -#define WHOWAS_UNUSED ((unsigned int)-1) + if (ww->online) { /* unlink from client */ + if (ww->cnext) /* shouldn't happen, but I'm not confident of that */ + ww->cnext->cprevnextp = ww->cprevnextp; + *ww->cprevnextp = ww->cnext; + } -/* - * add_history - * - * Add a client (cptr) that just changed nick (still_on == true), or - * just signed off (still_on == false) to the `whowas' table. - * - * If the entry used was already in use, then this entry is - * freed (lost). + if (ww->hnext) /* now unlink from hash table */ + ww->hnext->hprevnextp = ww->hprevnextp; + *ww->hprevnextp = ww->hnext; + + if (ww->wnext) /* unlink from whowas linked list... */ + ww->wnext->wprev = ww->wprev; + if (ww->wprev) + ww->wprev->wnext = ww->wnext; + + if (wwList.ww_tail == ww) /* update tail pointer appropriately */ + wwList.ww_tail = ww->wprev; + + /* Free old info */ + if (ww->name) + MyFree(ww->name); + if (ww->username) + MyFree(ww->username); + if (ww->hostname) + MyFree(ww->hostname); + if (ww->realhost) + MyFree(ww->realhost); + if (ww->servername) + MyFree(ww->servername); + if (ww->realname) + MyFree(ww->realname); + if (ww->away) + MyFree(ww->away); + + return ww; +} + +/** Clean and free a whowas record. + * @param[in] ww Whowas record to free. */ -void add_history(struct Client *cptr, int still_on) +static void +whowas_free(struct Whowas *ww) { - Current ww; - ww.newww = whowas_next; + if (!ww) + return; - /* If this entry has already been used, remove it from the lists */ - if (ww.newww->hashv != WHOWAS_UNUSED) - { - if (ww.oldww->online) /* No need to update cnext/cprev when offline! */ - { - /* Remove ww.oldww from the linked list with the same `online' pointers */ - *ww.oldww->cprevnextp = ww.oldww->cnext; + Debug((DEBUG_LIST, "Destroying whowas structure for %s", ww->name)); - assert(0 == ww.oldww->cnext); + whowas_clean(ww); + MyFree(ww); - } - /* Remove ww.oldww from the linked list with the same `hashv' */ - *ww.oldww->hprevnextp = ww.oldww->hnext; - - assert(0 == ww.oldww->hnext); - - if (ww.oldww->name) - MyFree(ww.oldww->name); - if (ww.oldww->username) - MyFree(ww.oldww->username); - if (ww.oldww->hostname) - MyFree(ww.oldww->hostname); - if (ww.oldww->servername) - MyFree(ww.oldww->servername); - if (ww.oldww->realname) - MyFree(ww.oldww->realname); - if (ww.oldww->away) - MyFree(ww.oldww->away); + wwList.ww_alloc--; +} + +/** Return a fresh Whowas record. + * If the total number of records is smaller than determined by + * FEAT_NICKNAMEHISTORYLENGTH, allocate a new one. Otherwise, + * reuse the oldest record in use. + * @return A pointer to a clean Whowas. + */ +static struct Whowas * +whowas_alloc(void) +{ + struct Whowas *ww; + + if (wwList.ww_alloc >= feature_int(FEAT_NICKNAMEHISTORYLENGTH)) { + /* reclaim the oldest whowas entry */ + ww = whowas_clean(wwList.ww_tail); + } else { + /* allocate a new one */ + wwList.ww_alloc++; + ww = (struct Whowas *) MyMalloc(sizeof(struct Whowas)); } - /* Initialize aWhoWas struct `newww' */ - ww.newww->hashv = hash_whowas_name(cptr->name); - ww.newww->logoff = CurrentTime; - DupString(ww.newww->name, cptr->name); - DupString(ww.newww->username, cptr->user->username); - DupString(ww.newww->hostname, cptr->user->host); - /* Should be changed to server numeric */ - DupString(ww.newww->servername, cptr->user->server->name); - DupString(ww.newww->realname, cptr->info); - if (cptr->user->away) - DupString(ww.newww->away, cptr->user->away); - else - ww.newww->away = NULL; - - /* Update/initialize online/cnext/cprev: */ - if (still_on) /* User just changed nicknames */ - { - ww.newww->online = cptr; - /* Add struct Whowas struct `newww' to start of 'online list': */ - if ((ww.newww->cnext = cptr->whowas)) - ww.newww->cnext->cprevnextp = &ww.newww->cnext; - ww.newww->cprevnextp = &cptr->whowas; - cptr->whowas = ww.newww; + assert(ww != NULL); + memset(ww, 0, sizeof(*ww)); + return ww; +} + +/** If necessary, trim the whowas list. + * This function trims the whowas list until it contains no more than + * FEAT_NICKNAMEHISTORYLENGTH records. + */ +void +whowas_realloc(void) +{ + Debug((DEBUG_LIST, "whowas_realloc() called with alloc count %d, " + "history length %d, tail pointer %p", wwList.ww_alloc, + feature_int(FEAT_NICKNAMEHISTORYLENGTH), wwList.ww_tail)); + + while (wwList.ww_alloc > feature_int(FEAT_NICKNAMEHISTORYLENGTH)) { + if (!wwList.ww_tail) { /* list is empty... */ + Debug((DEBUG_LIST, "whowas list emptied with alloc count %d", + wwList.ww_alloc)); + return; + } + + whowas_free(wwList.ww_tail); /* free oldest element of whowas list */ } - else /* User quitting */ - ww.newww->online = NULL; - - /* Add struct Whowas struct `newww' to start of 'hashv list': */ - if ((ww.newww->hnext = whowashash[ww.newww->hashv])) - ww.newww->hnext->hprevnextp = &ww.newww->hnext; - ww.newww->hprevnextp = &whowashash[ww.newww->hashv]; - whowashash[ww.newww->hashv] = ww.newww; - - /* Advance `whowas_next' to next entry in the `whowas' table: */ - if (++whowas_next == &whowas[NICKNAMEHISTORYLENGTH]) - whowas_next = whowas; } -/* - * off_history - * - * Client `cptr' signed off: Set all `online' pointers - * corresponding to this client to NULL. +/** Add a client to the whowas list. + * @param[in] cptr Client to add. + * @param[in] still_on If non-zero, link the record to the client's personal history. + */ +void add_history(struct Client *cptr, int still_on) +{ + struct Whowas *ww; + + if (!(ww = whowas_alloc())) + return; /* couldn't get a structure */ + + ww->hashv = hash_whowas_name(cli_name(cptr)); /* initialize struct */ + ww->logoff = CurrentTime; + DupString(ww->name, cli_name(cptr)); + DupString(ww->username, cli_user(cptr)->username); + DupString(ww->hostname, cli_user(cptr)->host); + if (HasHiddenHost(cptr)) + DupString(ww->realhost, cli_user(cptr)->realhost); + DupString(ww->servername, cli_name(cli_user(cptr)->server)); + DupString(ww->realname, cli_info(cptr)); + if (cli_user(cptr)->away) + DupString(ww->away, cli_user(cptr)->away); + + if (still_on) { /* user changed nicknames... */ + ww->online = cptr; + if ((ww->cnext = cli_whowas(cptr))) + ww->cnext->cprevnextp = &ww->cnext; + ww->cprevnextp = &(cli_whowas(cptr)); + cli_whowas(cptr) = ww; + } else /* user quit */ + ww->online = 0; + + /* link new whowas structure to list */ + ww->wnext = wwList.ww_list; + if (wwList.ww_list) + wwList.ww_list->wprev = ww; + wwList.ww_list = ww; + + if (!wwList.ww_tail) /* update the tail pointer... */ + wwList.ww_tail = ww; + + /* Now link it into the hash table */ + if ((ww->hnext = whowashash[ww->hashv])) + ww->hnext->hprevnextp = &ww->hnext; + ww->hprevnextp = &whowashash[ww->hashv]; + whowashash[ww->hashv] = ww; +} + +/** Clear all Whowas::online pointers that point to a client. + * @param[in] cptr Client who is going offline. */ void off_history(const struct Client *cptr) { struct Whowas *temp; - for (temp = cptr->whowas; temp; temp = temp->cnext) + for (temp = cli_whowas(cptr); temp; temp = temp->cnext) temp->online = NULL; } -/* - * get_history - * - * Return a pointer to a client that had nick `nick' not more then - * `timelimit' seconds ago, if still on line. Otherwise return NULL. - * - * This function is used for "nick chasing"; since the use of numeric - * nicks for "upstream" messages in ircu2.10, this is only used for - * looking up non-existing nicks in client->server messages. +/** Find a client who has recently used a particular nickname. + * @param[in] nick Nickname to find. + * @param[in] timelimit Maximum age for entry. + * @return User's online client, or NULL if none is found. */ struct Client *get_history(const char *nick, time_t timelimit) { @@ -255,10 +332,16 @@ struct Client *get_history(const char *nick, time_t timelimit) return NULL; } +/** Count memory used by whowas list. + * @param[out] wwu Number of entries in whowas list. + * @param[out] wwum Total number of bytes used by nickname, username, + * hostname and servername fields. + * @param[out] wwa Number of away strings in whowas list. + * @param[out] wwam Total number of bytes used by away strings. + */ void count_whowas_memory(int *wwu, size_t *wwum, int *wwa, size_t *wwam) { struct Whowas *tmp; - int i; int u = 0; int a = 0; size_t um = 0; @@ -268,17 +351,15 @@ void count_whowas_memory(int *wwu, size_t *wwum, int *wwa, size_t *wwam) assert(0 != wwa); assert(0 != wwam); - for (i = 0, tmp = whowas; i < NICKNAMEHISTORYLENGTH; i++, tmp++) { - if (tmp->hashv != WHOWAS_UNUSED) { - u++; - um += (strlen(tmp->name) + 1); - um += (strlen(tmp->username) + 1); - um += (strlen(tmp->hostname) + 1); - um += (strlen(tmp->servername) + 1); - if (tmp->away) { - a++; - am += (strlen(tmp->away) + 1); - } + for (tmp = wwList.ww_list; tmp; tmp = tmp->wnext) { + u++; + um += (strlen(tmp->name) + 1); + um += (strlen(tmp->username) + 1); + um += (strlen(tmp->hostname) + 1); + um += (strlen(tmp->servername) + 1); + if (tmp->away) { + a++; + am += (strlen(tmp->away) + 1); } } *wwu = u; @@ -287,15 +368,19 @@ void count_whowas_memory(int *wwu, size_t *wwum, int *wwa, size_t *wwam) *wwam = am; } - +/** Initialize whowas table. */ void initwhowas(void) { int i; - for (i = 0; i < NICKNAMEHISTORYLENGTH; i++) - whowas[i].hashv = WHOWAS_UNUSED; + for (i = 0; i < WW_MAX; i++) + whowashash[i] = 0; } +/** Calculate a hash value for a string. + * @param[in] name Nickname to calculate hash over. + * @return Calculated hash value. + */ unsigned int hash_whowas_name(const char *name) { unsigned int hash = 0;