*
* $Id$
*/
-#include "list.h"
+#include "config.h"
-#include "class.h"
+#include "list.h"
#include "client.h"
#include "ircd.h"
#include "ircd_alloc.h"
+#include "ircd_events.h"
+#include "ircd_reply.h"
#include "ircd_string.h"
#include "listener.h"
#include "match.h"
#include "numeric.h"
#include "res.h"
+#include "s_auth.h"
#include "s_bsd.h"
#include "s_conf.h"
#include "s_debug.h"
#include "s_user.h"
#include "send.h"
#include "struct.h"
-#include "support.h"
#include "whowas.h"
#include <assert.h>
#ifdef DEBUGMODE
static struct liststats {
int inuse;
-} cloc, crem, users, servs, links, classs;
+} clients, connections, users, servs, links;
#endif
+static unsigned int clientAllocCount;
+static struct Client* clientFreeList;
+
+static unsigned int connectionAllocCount;
+static struct Connection* connectionFreeList;
+
+static unsigned int slinkAllocCount;
+static struct SLink* slinkFreeList;
+
void init_list(void)
{
+ struct Client* cptr;
+ struct Connection* con;
+ int i;
+ /*
+ * pre-allocate MAXCONNECTIONS clients and connections
+ */
+ for (i = 0; i < MAXCONNECTIONS; ++i) {
+ cptr = (struct Client*) MyMalloc(sizeof(struct Client));
+ cli_next(cptr) = clientFreeList;
+ clientFreeList = cptr;
+ ++clientAllocCount;
+
+ con = (struct Connection*) MyMalloc(sizeof(struct Connection));
+ con_next(con) = connectionFreeList;
+ connectionFreeList = con;
+ ++connectionAllocCount;
+ }
+
#ifdef DEBUGMODE
- memset(&cloc, 0, sizeof(cloc));
- memset(&crem, 0, sizeof(crem));
+ memset(&clients, 0, sizeof(clients));
+ memset(&connections, 0, sizeof(connections));
memset(&users, 0, sizeof(users));
memset(&servs, 0, sizeof(servs));
memset(&links, 0, sizeof(links));
- memset(&classs, 0, sizeof(classs));
#endif
}
+static struct Client* alloc_client(void)
+{
+ struct Client* cptr = clientFreeList;
+
+ if (!cptr) {
+ cptr = (struct Client*) MyMalloc(sizeof(struct Client));
+ ++clientAllocCount;
+ } else
+ clientFreeList = cli_next(cptr);
+
+#ifdef DEBUGMODE
+ clients.inuse++;
+#endif
+
+ memset(cptr, 0, sizeof(struct Client));
+
+ return cptr;
+}
+
+static void dealloc_client(struct Client* cptr)
+{
+ assert(cli_verify(cptr));
+ assert(0 == cli_connect(cptr));
+
+#ifdef DEBUGMODE
+ --clients.inuse;
+#endif
+
+ cli_next(cptr) = clientFreeList;
+ clientFreeList = cptr;
+
+ cli_magic(cptr) = 0;
+}
+
+static struct Connection* alloc_connection(void)
+{
+ struct Connection* con = connectionFreeList;
+
+ if (!con) {
+ con = (struct Connection*) MyMalloc(sizeof(struct Connection));
+ ++connectionAllocCount;
+ } else
+ connectionFreeList = con_next(con);
+
+#ifdef DEBUGMODE
+ connections.inuse++;
+#endif
+
+ memset(con, 0, sizeof(struct Connection));
+ timer_init(&(con_proc(con)));
+
+ return con;
+}
+
+static void dealloc_connection(struct Connection* con)
+{
+ assert(con_verify(con));
+ assert(!t_active(&(con_proc(con))));
+ assert(!t_onqueue(&(con_proc(con))));
+
+ Debug((DEBUG_LIST, "Deallocating connection %p", con));
+
+
+ if (con_dns_reply(con)) {
+ MyFree(con_dns_reply(con));
+ con_dns_reply(con) = 0;
+ }
+ if (-1 < con_fd(con))
+ close(con_fd(con));
+ MsgQClear(&(con_sendQ(con)));
+ client_drop_sendq(con);
+ DBufClear(&(con_recvQ(con)));
+ if (con_listener(con))
+ release_listener(con_listener(con));
+
+#ifdef DEBUGMODE
+ --connections.inuse;
+#endif
+
+ con_next(con) = connectionFreeList;
+ connectionFreeList = con;
+
+ con_magic(con) = 0;
+}
+
/*
* Create a new struct Client structure and set it to initial state.
*
*/
struct Client* make_client(struct Client *from, int status)
{
- struct Client *cptr = NULL;
- size_t size = CLIENT_REMOTE_SIZE;
+ struct Client* cptr = 0;
+ struct Connection* con = 0;
- /*
- * Check freelists first to see if we can grab a client without
- * having to call malloc.
- */
- if (!from)
- size = CLIENT_LOCAL_SIZE;
+ assert(!from || cli_verify(from));
- cptr = (struct Client*) MyMalloc(size);
- assert(0 != cptr);
- /*
- * NOTE: Do not remove this, a lot of code depends on the entire
- * structure being zeroed out
- */
- memset(cptr, 0, size); /* All variables are 0 by default */
+ cptr = alloc_client();
-#ifdef DEBUGMODE
- if (size == CLIENT_LOCAL_SIZE)
- cloc.inuse++;
- else
- crem.inuse++;
-#endif
+ assert(0 != cptr);
+ assert(!cli_magic(cptr));
+ assert(0 == from || 0 != cli_connect(from));
+
+ if (!from) { /* local client, allocate a struct Connection */
+ con = alloc_connection();
+
+ assert(0 != con);
+ assert(!con_magic(con));
+
+ con_magic(con) = CONNECTION_MAGIC;
+ con_fd(con) = -1; /* initialize struct Connection */
+ con_freeflag(con) = 0;
+ con_nextnick(con) = CurrentTime - NICK_DELAY;
+ con_nexttarget(con) = CurrentTime - (TARGET_DELAY * (STARTTARGETS - 1));
+ con_handler(con) = UNREGISTERED_HANDLER;
+ con_client(con) = cptr;
+
+ cli_local(cptr) = 1; /* Set certain fields of the struct Client */
+ cli_since(cptr) = cli_lasttime(cptr) = cli_firsttime(cptr) = CurrentTime;
+ cli_lastnick(cptr) = TStime();
+ } else
+ con = cli_connect(from); /* use 'from's connection */
+
+ assert(0 != con);
+ assert(con_verify(con));
+
+ cli_magic(cptr) = CLIENT_MAGIC;
+ cli_connect(cptr) = con; /* set the connection and other fields */
+ cli_status(cptr) = status;
+ cli_hnext(cptr) = cptr;
+ strcpy(cli_username(cptr), "unknown");
- /* Note: structure is zero (memset) */
- cptr->from = from ? from : cptr; /* 'from' of local client is self! */
- cptr->status = status;
- cptr->hnext = cptr;
- strcpy(cptr->username, "unknown");
-
- if (CLIENT_LOCAL_SIZE == size) {
- cptr->fd = -1;
- cptr->local = 1;
- cptr->since = cptr->lasttime = cptr->firsttime = CurrentTime;
- cptr->lastnick = TStime();
- cptr->nextnick = CurrentTime - NICK_DELAY;
- cptr->nexttarget = CurrentTime - (TARGET_DELAY * (STARTTARGETS - 1));
- cptr->handler = UNREGISTERED_HANDLER;
- }
return cptr;
}
-void free_client(struct Client *cptr)
+void free_connection(struct Connection* con)
+{
+ if (!con)
+ return;
+
+ assert(con_verify(con));
+ assert(0 == con_client(con));
+
+ dealloc_connection(con); /* deallocate the connection */
+}
+
+void free_client(struct Client* cptr)
{
- if (cptr && cptr->local) {
- /*
- * make sure we have cleaned up local resources
- */
- if (cptr->dns_reply)
- --cptr->dns_reply->ref_count;
- if (-1 < cptr->fd)
- close(cptr->fd);
- DBufClear(&cptr->sendQ);
- DBufClear(&cptr->recvQ);
- if (cptr->listener)
- release_listener(cptr->listener);
- }
+ if (!cptr)
+ return;
/*
* forget to remove the client from the hash table?
*/
- assert(cptr->hnext == cptr);
+ assert(cli_verify(cptr));
+ assert(cli_hnext(cptr) == cptr);
+ /* or from linked list? */
+ assert(cli_next(cptr) == 0);
+ assert(cli_prev(cptr) == 0);
+
+ Debug((DEBUG_LIST, "Freeing client %s [%p], connection %p", cli_name(cptr),
+ cptr, cli_connect(cptr)));
+
+ if (cli_auth(cptr))
+ destroy_auth_request(cli_auth(cptr), 0);
+
+ /* Make sure we didn't magically get re-added to the list */
+ assert(cli_next(cptr) == 0);
+ assert(cli_prev(cptr) == 0);
+
+ if (cli_from(cptr) == cptr) { /* in other words, we're local */
+ cli_from(cptr) = 0;
+ /* timer must be marked as not active */
+ if (!cli_freeflag(cptr) && !t_active(&(cli_proc(cptr))))
+ dealloc_connection(cli_connect(cptr)); /* connection not open anymore */
+ else {
+ if (-1 < cli_fd(cptr) && cli_freeflag(cptr) & FREEFLAG_SOCKET)
+ socket_del(&(cli_socket(cptr))); /* queue a socket delete */
+ if (cli_freeflag(cptr) & FREEFLAG_TIMER)
+ timer_del(&(cli_proc(cptr))); /* queue a timer delete */
+ }
+ }
+
+ cli_connect(cptr) = 0;
- MyFree(cptr);
+ dealloc_client(cptr); /* actually destroy the client */
}
struct Server *make_server(struct Client *cptr)
{
- struct Server *serv = cptr->serv;
+ struct Server *serv = cli_serv(cptr);
+
+ assert(cli_verify(cptr));
if (!serv)
{
#ifdef DEBUGMODE
servs.inuse++;
#endif
- cptr->serv = serv;
- cptr->serv->lag = 10000;
+ cli_serv(cptr) = serv;
+ cli_serv(cptr)->lag = 60000;
*serv->by = '\0';
DupString(serv->last_error_msg, "<>"); /* String must be non-empty */
}
- return cptr->serv;
+ return cli_serv(cptr);
}
/*
*/
void remove_client_from_list(struct Client *cptr)
{
- if (cptr->prev)
- cptr->prev->next = cptr->next;
- else {
- GlobalClientList = cptr->next;
- GlobalClientList->prev = 0;
+ assert(cli_verify(cptr));
+ assert(con_verify(cli_connect(cptr)));
+ assert(!cli_prev(cptr) || cli_verify(cli_prev(cptr)));
+ assert(!cli_next(cptr) || cli_verify(cli_next(cptr)));
+ assert(!IsMe(cptr));
+
+ /* Only try remove cptr from the list if it IS in the list.
+ * cli_next(cptr) cannot be NULL here, as &me is always the end
+ * the list, and we never remove &me. -GW
+ */
+ if(cli_next(cptr))
+ {
+ if (cli_prev(cptr))
+ cli_next(cli_prev(cptr)) = cli_next(cptr);
+ else {
+ GlobalClientList = cli_next(cptr);
+ cli_prev(GlobalClientList) = 0;
+ }
+ cli_prev(cli_next(cptr)) = cli_prev(cptr);
}
- if (cptr->next)
- cptr->next->prev = cptr->prev;
-
- cptr->next = cptr->prev = 0;
+ cli_next(cptr) = cli_prev(cptr) = 0;
- if (IsUser(cptr) && cptr->user) {
+ if (IsUser(cptr) && cli_user(cptr)) {
add_history(cptr, 0);
off_history(cptr);
}
- if (cptr->user) {
- free_user(cptr->user);
- cptr->user = 0;
+ if (cli_user(cptr)) {
+ free_user(cli_user(cptr));
+ cli_user(cptr) = 0;
}
- if (cptr->serv) {
- if (cptr->serv->user) {
- free_user(cptr->serv->user);
- cptr->serv->user = 0;
+ if (cli_serv(cptr)) {
+ if (cli_serv(cptr)->user) {
+ free_user(cli_serv(cptr)->user);
+ cli_serv(cptr)->user = 0;
}
- if (cptr->serv->client_list)
- MyFree(cptr->serv->client_list);
- MyFree(cptr->serv->last_error_msg);
- MyFree(cptr->serv);
+ if (cli_serv(cptr)->client_list)
+ MyFree(cli_serv(cptr)->client_list);
+ MyFree(cli_serv(cptr)->last_error_msg);
+ MyFree(cli_serv(cptr));
#ifdef DEBUGMODE
--servs.inuse;
#endif
}
-#ifdef DEBUGMODE
- if (cptr->local)
- --cloc.inuse;
- else
- --crem.inuse;
-#endif
free_client(cptr);
}
*/
void add_client_to_list(struct Client *cptr)
{
+ assert(cli_verify(cptr));
+ assert(cli_next(cptr) == 0);
+ assert(cli_prev(cptr) == 0);
+
/*
* Since we always insert new clients to the top of the list,
* this should mean the "me" is the bottom most item in the list.
* XXX - don't always count on the above, things change
*/
- cptr->prev = 0;
- cptr->next = GlobalClientList;
+ cli_prev(cptr) = 0;
+ cli_next(cptr) = GlobalClientList;
GlobalClientList = cptr;
- if (cptr->next)
- cptr->next->prev = cptr;
+ if (cli_next(cptr))
+ cli_prev(cli_next(cptr)) = cptr;
}
+#if 0
+/* WARNING: Major CPU sink!
+ *
+ * This is a debugging routine meant to verify the integrity of the client
+ * linked list. It is meant to be comprehensive, to detect *any* corruption
+ * of that list. This means that it will be majorly CPU-intensive, and
+ * should *only* be enabled on servers that have DEBUGMODE enabled. Ignore
+ * this warning at your peril!
+ */
+void verify_client_list(void)
+{
+ struct Client *client, *prev = 0, *sentinel = 0;
+ extern unsigned int ircrandom(void);
+
+ for (client = GlobalClientList; client; client = cli_next(client)) {
+ /* Verify that this is a valid client, not a free'd one */
+ assert(cli_verify(client));
+ /* Verify that the list hasn't suddenly jumped around */
+ assert(cli_prev(client) == prev);
+ /* Verify that the list hasn't become circular */
+ assert(cli_next(client) != GlobalClientList);
+ assert(!sentinel || client != sentinel);
+
+ prev = client; /* Remember what should preceed us */
+ if (!(ircrandom() % 50)) /* probabilistic loop detector */
+ sentinel = client;
+ }
+}
+#endif /* DEBUGMODE */
+
/*
* Look for ptr in the linked listed pointed to by link.
*/
struct SLink *find_user_link(struct SLink *lp, struct Client *ptr)
{
- if (ptr)
- while (lp)
- {
+ if (ptr) {
+ while (lp) {
if (lp->value.cptr == ptr)
return (lp);
lp = lp->next;
}
+ }
return NULL;
}
-struct SLink *make_link(void)
+struct SLink* make_link(void)
{
- struct SLink *lp;
-
- lp = (struct SLink*) MyMalloc(sizeof(struct SLink));
+ struct SLink* lp = slinkFreeList;
+ if (lp)
+ slinkFreeList = lp->next;
+ else {
+ lp = (struct SLink*) MyMalloc(sizeof(struct SLink));
+ ++slinkAllocCount;
+ }
assert(0 != lp);
#ifdef DEBUGMODE
links.inuse++;
return lp;
}
-void free_link(struct SLink *lp)
+void free_link(struct SLink* lp)
{
- MyFree(lp);
+ if (lp) {
+ lp->next = slinkFreeList;
+ slinkFreeList = lp;
+ }
#ifdef DEBUGMODE
links.inuse--;
#endif
MyFree(lp);
}
-struct ConfClass *make_class(void)
-{
- struct ConfClass *tmp;
-
- tmp = (struct ConfClass*) MyMalloc(sizeof(struct ConfClass));
- assert(0 != tmp);
-#ifdef DEBUGMODE
- classs.inuse++;
-#endif
- return tmp;
-}
-
-void free_class(struct ConfClass * tmp)
-{
- MyFree(tmp);
-#ifdef DEBUGMODE
- classs.inuse--;
-#endif
-}
-
#ifdef DEBUGMODE
void send_listinfo(struct Client *cptr, char *name)
{
int inuse = 0, mem = 0, tmp = 0;
- sendto_one(cptr, ":%s %d %s :Local: inuse: %d(%d)",
- me.name, RPL_STATSDEBUG, name, inuse += cloc.inuse,
- tmp = cloc.inuse * CLIENT_LOCAL_SIZE);
- mem += tmp;
- sendto_one(cptr, ":%s %d %s :Remote: inuse: %d(%d)",
- me.name, RPL_STATSDEBUG, name,
- crem.inuse, tmp = crem.inuse * CLIENT_REMOTE_SIZE);
+ send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":Clients: inuse: %d(%d)",
+ clients.inuse, tmp = clients.inuse * sizeof(struct Client));
mem += tmp;
- inuse += crem.inuse;
- sendto_one(cptr, ":%s %d %s :Users: inuse: %d(%d)",
- me.name, RPL_STATSDEBUG, name, users.inuse,
- tmp = users.inuse * sizeof(struct User));
+ inuse += clients.inuse;
+ send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, "Connections: inuse: %d(%d)",
+ connections.inuse,
+ tmp = connections.inuse * sizeof(struct Connection));
mem += tmp;
- inuse += users.inuse,
- sendto_one(cptr, ":%s %d %s :Servs: inuse: %d(%d)",
- me.name, RPL_STATSDEBUG, name, servs.inuse,
- tmp = servs.inuse * sizeof(struct Server));
+ inuse += connections.inuse;
+ send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":Users: inuse: %d(%d)",
+ users.inuse, tmp = users.inuse * sizeof(struct User));
mem += tmp;
- inuse += servs.inuse,
- sendto_one(cptr, ":%s %d %s :Links: inuse: %d(%d)",
- me.name, RPL_STATSDEBUG, name, links.inuse,
- tmp = links.inuse * sizeof(struct SLink));
+ inuse += users.inuse;
+ send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":Servs: inuse: %d(%d)",
+ servs.inuse, tmp = servs.inuse * sizeof(struct Server));
mem += tmp;
- inuse += links.inuse,
- sendto_one(cptr, ":%s %d %s :Classes: inuse: %d(%d)",
- me.name, RPL_STATSDEBUG, name, classs.inuse,
- tmp = classs.inuse * sizeof(struct ConfClass));
+ inuse += servs.inuse;
+ send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":Links: inuse: %d(%d)",
+ links.inuse, tmp = links.inuse * sizeof(struct SLink));
mem += tmp;
- inuse += classs.inuse,
- sendto_one(cptr, ":%s %d %s :Confs: inuse: %d(%d)",
- me.name, RPL_STATSDEBUG, name, GlobalConfCount,
- tmp = GlobalConfCount * sizeof(struct ConfItem));
+ inuse += links.inuse;
+ send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":Confs: inuse: %d(%d)",
+ GlobalConfCount, tmp = GlobalConfCount * sizeof(struct ConfItem));
mem += tmp;
- inuse += GlobalConfCount,
- sendto_one(cptr, ":%s %d %s :Totals: inuse %d %d",
- me.name, RPL_STATSDEBUG, name, inuse, mem);
+ inuse += GlobalConfCount;
+ send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":Totals: inuse %d %d",
+ inuse, mem);
}
#endif