*
* $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 localClientAllocCount;
-static struct Client* localClientFreeList;
+static unsigned int clientAllocCount;
+static struct Client* clientFreeList;
-static unsigned int remoteClientAllocCount;
-static struct Client* remoteClientFreeList;
+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 local clients
+ * pre-allocate MAXCONNECTIONS clients and connections
*/
for (i = 0; i < MAXCONNECTIONS; ++i) {
- cptr = (struct Client*) MyMalloc(CLIENT_LOCAL_SIZE);
- cptr->next = localClientFreeList;
- localClientFreeList = cptr;
- ++localClientAllocCount;
+ 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 = 0;
- /*
- * Check freelists first to see if we can grab a client without
- * having to call malloc.
- */
- if (from) {
- /*
- * remote client
- */
- if ((cptr = remoteClientFreeList))
- remoteClientFreeList = cptr->next;
- else {
- cptr = (struct Client*) MyMalloc(CLIENT_REMOTE_SIZE);
- ++remoteClientAllocCount;
- }
- assert(0 != cptr);
- /*
- * NOTE: Do not remove this, a lot of code depends on the entire
- * structure being zeroed out
- */
- memset(cptr, 0, CLIENT_REMOTE_SIZE); /* All variables are 0 by default */
- cptr->from = from;
- }
- else {
- /*
- * local client
- */
- if ((cptr = localClientFreeList))
- localClientFreeList = cptr->next;
- else {
- cptr = (struct Client*) MyMalloc(CLIENT_LOCAL_SIZE);
- ++localClientAllocCount;
- }
- assert(0 != cptr);
- /*
- * NOTE: Do not remove this, a lot of code depends on the entire
- * structure being zeroed out
- */
- memset(cptr, 0, CLIENT_LOCAL_SIZE); /* All variables are 0 by default */
- 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;
- cptr->from = cptr; /* 'from' of local client is self! */
- }
- cptr->status = status;
- cptr->hnext = cptr;
- strcpy(cptr->username, "unknown");
+ struct Connection* con = 0;
-#ifdef DEBUGMODE
- if (from)
- crem.inuse++;
- else
- cloc.inuse++;
-#endif
+ assert(!from || cli_verify(from));
+
+ cptr = alloc_client();
+
+ 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");
return 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)
/*
* forget to remove the client from the hash table?
*/
- assert(cptr->hnext == cptr);
-
-#ifdef DEBUGMODE
- if (cptr->local)
- --cloc.inuse;
- else
- --crem.inuse;
-#endif
-
- if (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);
+ 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 */
}
- DBufClear(&cptr->sendQ);
- DBufClear(&cptr->recvQ);
- if (cptr->listener)
- release_listener(cptr->listener);
- cptr->next = localClientFreeList;
- localClientFreeList = cptr;
- }
- else {
- cptr->next = remoteClientFreeList;
- remoteClientFreeList = cptr;
}
+
+ cli_connect(cptr) = 0;
+
+ 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 = 60000;
+ 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
*/
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.
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;
- send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":Local: inuse: %d(%d)",
- inuse += cloc.inuse, tmp = cloc.inuse * CLIENT_LOCAL_SIZE);
+ send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":Clients: inuse: %d(%d)",
+ clients.inuse, tmp = clients.inuse * sizeof(struct Client));
mem += tmp;
- send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":Remote: inuse: %d(%d)",
- crem.inuse, tmp = crem.inuse * CLIENT_REMOTE_SIZE);
+ 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 += crem.inuse;
+ inuse += connections.inuse;
send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":Users: inuse: %d(%d)",
users.inuse, tmp = users.inuse * sizeof(struct User));
mem += tmp;
links.inuse, tmp = links.inuse * sizeof(struct SLink));
mem += tmp;
inuse += links.inuse;
- send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":Classes: inuse: %d(%d)",
- classs.inuse, tmp = classs.inuse * sizeof(struct ConfClass));
- mem += tmp;
- inuse += classs.inuse;
send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":Confs: inuse: %d(%d)",
GlobalConfCount, tmp = GlobalConfCount * sizeof(struct ConfItem));
mem += tmp;