/************************************************************************
* IRC - Internet Relay Chat, src/list.c
* Copyright (C) 1990 Jarkko Oikarinen and
* University of Oulu, Finland
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* $Id: list.c,v 1.4 2005/07/05 21:53:41 sheik Exp $ */
#include "struct.h"
#include "common.h"
#include "sys.h"
#include "h.h"
#include "numeric.h"
#include "blalloc.h"
#include "dh.h"
#include "zlink.h"
#include "memcount.h"
extern int BlockHeapGarbageCollect(BlockHeap *);
/* locally defined functions */
/*
* re-written to use Wohali (joant@cadence.com) block allocator
* routines. very nicely done Wohali
*
* -Dianora
*
* Number of Link's to pre-allocate at a time for Efnet 1000 seems
* reasonable, for smaller nets who knows? -Dianora
*/
#define LINK_PREALLOCATE 1024
#define DLINK_PREALLOCATE 128
#define CHANMEMBER_PREALLOCATE 1024
/*
* Number of aClient structures to preallocate at a time for Efnet 1024
* is reasonable for smaller nets who knows? -Dianora
* This means you call MyMalloc 30 some odd times, rather than 30k
* times -Dianora
*/
#define CLIENTS_PREALLOCATE 1024
#if CLIENTS_PREALLOCATE > MAXCONNECTIONS
#undef CLIENTS_PREALLOCATE
#define CLIENTS_PREALLOCATE MAXCONNECTIONS
#endif
/* Number of channels to allocate per block, 1024 sounds nice. */
#define CHANNELS_PREALLOCATE 1024
void outofmemory();
int numclients = 0;
/* for jolo's block allocator */
BlockHeap *free_local_aClients;
BlockHeap *free_Links;
BlockHeap *free_DLinks;
BlockHeap *free_chanMembers;
BlockHeap *free_remote_aClients;
BlockHeap *free_anUsers;
BlockHeap *free_channels;
#ifdef FLUD
BlockHeap *free_fludbots;
#endif /* FLUD */
void initlists()
{
/* Might want to bump up LINK_PREALLOCATE if FLUD is defined */
free_Links = BlockHeapCreate((size_t) sizeof(Link), LINK_PREALLOCATE);
free_DLinks = BlockHeapCreate((size_t) sizeof(DLink), DLINK_PREALLOCATE);
free_chanMembers = BlockHeapCreate((size_t) sizeof(chanMember),
CHANMEMBER_PREALLOCATE);
/*
* start off with CLIENTS_PREALLOCATE for now... on typical efnet
* these days, it can get up to 35k allocated
*/
free_remote_aClients =
BlockHeapCreate((size_t) CLIENT_REMOTE_SIZE, CLIENTS_PREALLOCATE);
/* Can't EVER have more than MAXCONNECTIONS number of local aClients
* And that was stupid, because an idle server built for 32k would use
* 50 megs of ram.
*/
free_local_aClients = BlockHeapCreate((size_t) CLIENT_LOCAL_SIZE,
CLIENTS_PREALLOCATE);
/* anUser structs are used by both local aClients, and remote aClients */
free_anUsers = BlockHeapCreate((size_t) sizeof(anUser),
CLIENTS_PREALLOCATE * 2);
/* channels are a very frequent thing in ircd. :) */
free_channels = BlockHeapCreate((size_t) sizeof(aChannel),
CHANNELS_PREALLOCATE);
#ifdef FLUD
/* fludbot structs are used to track CTCP Flooders */
free_fludbots = BlockHeapCreate((size_t) sizeof(struct fludbot),
CLIENTS_PREALLOCATE);
#endif /* FLUD */
}
/*
* outofmemory()
*
* input - NONE output - NONE side effects -
* simply try to report there is a problem I free all the memory in the
* kline lists hoping to free enough memory so that a proper report can
* be made. If I was already here (was_here) then I got called twice,
* and more drastic measures are in order. I'll try to just abort() at
* least. -Dianora
*/
void outofmemory()
{
static int was_here = 0;
if (was_here)
abort();
was_here = YES;
Debug((DEBUG_FATAL, "Out of memory: restarting server..."));
restart("Out of Memory");
}
/*
* Create a new aClient structure and set it to initial state. *
*
* from == NULL, create local client (a client connected *
* o a socket). *
*
* from, create remote client (behind a socket *
* ssociated with the client defined by *
* ('from' is a local client!!).
*
* uplink is this client's uplink connection to the network. - lucas
*/
aClient *make_client(aClient *from, aClient *uplink)
{
aClient *cptr = NULL;
if (!from) /* from is NULL */
{
cptr = BlockHeapALLOC(free_local_aClients, aClient);
if (cptr == (aClient *) NULL)
outofmemory();
memset((char *) cptr, '\0' ,CLIENT_LOCAL_SIZE);
/* Note: structure is zero (calloc) */
cptr->from = cptr; /* 'from' of local client is self! */
cptr->status = STAT_UNKNOWN;
cptr->fd = -2;
cptr->uplink = uplink;
strcpy(cptr->username, "unknown");
cptr->since = cptr->lasttime = cptr->firsttime = timeofday;
cptr->sockerr = -1;
cptr->authfd = -1;
return (cptr);
}
else /* from is not NULL */
{
cptr = BlockHeapALLOC(free_remote_aClients, aClient);
if (cptr == (aClient *) NULL)
outofmemory();
memset((char *) cptr, '\0', CLIENT_REMOTE_SIZE);
/* Note: structure is zero (calloc) */
cptr->from = from;
cptr->status = STAT_UNKNOWN;
cptr->fd = -1;
cptr->uplink = uplink;
return (cptr);
}
}
void free_client(aClient *cptr)
{
int retval = 0;
if (cptr->fd == -2)
{
retval = BlockHeapFree(free_local_aClients, cptr);
}
else
{
retval = BlockHeapFree(free_remote_aClients, cptr);
}
if (retval)
{
/*
* Looks unprofessional maybe, but I am going to leave this
* sendto_ops in it should never happen, and if it does, the
* hybrid team wants to hear about it
*/
sendto_ops("list.c couldn't BlockHeapFree(free_remote_aClients,cptr) "
"cptr = %lX", cptr);
sendto_ops("Please report to the bahamut team! "
"coders@dal.net");
abort();
#if defined(USE_SYSLOG) && defined(SYSLOG_BLOCK_ALLOCATOR)
syslog(LOG_DEBUG, "list.c couldn't "
"BlockHeapFree(free_remote_aClients,cptr) cptr = %lX", cptr);
#endif
}
}
/*
* make_channel() free_channel()
* functions to maintain blockheap of channels.
*/
aChannel *make_channel()
{
aChannel *chan;
chan = BlockHeapALLOC(free_channels, aChannel);
if(chan == NULL)
outofmemory();
memset((char *)chan, '\0', sizeof(aChannel));
return chan;
}
void free_channel(aChannel *chan)
{
if (BlockHeapFree(free_channels, chan)) {
sendto_ops("list.c couldn't BlockHeapFree(free_channels,chan) "
"chan = %lX", chan);
sendto_ops("Please report to the bahamut team!");
}
}
/*
* * 'make_user' add's an User information block to a client * if it
* was not previously allocated.
*/
anUser *make_user(aClient *cptr)
{
anUser *user;
user = cptr->user;
if (!user)
{
user = BlockHeapALLOC(free_anUsers, anUser);
if (user == (anUser *) NULL)
outofmemory();
memset(user, 0, sizeof(anUser));
cptr->user = user;
}
return user;
}
aServer *make_server(aClient *cptr)
{
aServer *serv = cptr->serv;
if (!serv)
{
serv = (aServer *) MyMalloc(sizeof(aServer));
memset(serv, 0, sizeof(aServer));
cptr->serv = serv;
}
return cptr->serv;
}
/*
* free_user
* Decrease user reference count by one and release block,
* if count reaches 0
*/
void free_user(anUser *user, aClient *cptr)
{
if (user->away)
MyFree(user->away);
#if (RIDICULOUS_PARANOIA_LEVEL>=1)
if (user->real_oper_host)
MyFree(user->real_oper_host);
if (user->real_oper_username)
MyFree(user->real_oper_username);
if (user->real_oper_ip)
MyFree(user->real_oper_ip);
#endif
/* sanity check */
if (user->joined || user->invited || user->channel)
sendto_ops("* %#x user (%s!%s@%s) %#x %#x %#x %d *",
cptr, cptr ? cptr->name : "<noname>",
user->username, user->host, user,
user->invited, user->channel, user->joined);
if (BlockHeapFree(free_anUsers, user))
{
sendto_ops("list.c couldn't BlockHeapFree(free_anUsers,user) "
"user = %lX", user);
sendto_ops("Please report to the bahamut team! "
"bahamut-bugs@bahamut.net");
#if defined(USE_SYSLOG) && defined(SYSLOG_BLOCK_ALLOCATOR)
syslog(LOG_DEBUG, "list.c couldn't BlockHeapFree(free_anUsers,user) "
"user = %lX", user);
#endif
}
}
/* taken the code from ExitOneClient() for this and placed it here. - avalon */
void remove_client_from_list(aClient *cptr)
{
if (IsServer(cptr))
Count.server--;
else if (IsClient(cptr))
{
Count.total--;
if (IsAnOper(cptr))
Count.oper--;
if (IsInvisible(cptr))
Count.invisi--;
}
if (cptr->prev)
cptr->prev->next = cptr->next;
else
{
client = cptr->next;
client->prev = NULL;
}
if (cptr->next)
cptr->next->prev = cptr->prev;
if (IsPerson(cptr) && cptr->user)
{
add_history(cptr, 0);
off_history(cptr);
}
#ifdef FLUD
if (MyFludConnect(cptr))
free_fluders(cptr, NULL);
free_fludees(cptr);
#endif
if (cptr->user)
free_user(cptr->user, cptr); /* try this here */
if (cptr->serv)
{
#ifdef HAVE_ENCRYPTION_ON
if(cptr->serv->sessioninfo_in)
dh_end_session(cptr->serv->sessioninfo_in);
if(cptr->serv->sessioninfo_out)
dh_end_session(cptr->serv->sessioninfo_out);
if(cptr->serv->rc4_in)
rc4_destroystate(cptr->serv->rc4_in);
if(cptr->serv->rc4_out)
rc4_destroystate(cptr->serv->rc4_out);
#endif
if(cptr->serv->zip_in)
zip_destroy_input_session(cptr->serv->zip_in);
if(cptr->serv->zip_out)
zip_destroy_output_session(cptr->serv->zip_out);
MyFree(cptr->serv);
}
free_client(cptr);
return;
}
/*
* although only a small routine, it appears in a number of places as a
* collection of a few lines...functions like this *should* be in this
* file, shouldnt they ? after all, this is list.c, isnt it ? -avalon
*/
void add_client_to_list(aClient *cptr)
{
/*
* 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.
*/
cptr->next = client;
client = cptr;
if (cptr->next)
cptr->next->prev = cptr;
return;
}
/* Look for ptr in the linked listed pointed to by link. */
chanMember *find_user_member(chanMember *cm, aClient *ptr)
{
if (ptr)
while (cm)
{
if (cm->cptr == ptr)
return (cm);
cm = cm->next;
}
return ((chanMember *) NULL);
}
Link *find_channel_link(Link *lp, aChannel *chptr)
{
if (chptr)
for (; lp; lp = lp->next)
if (lp->value.chptr == chptr)
return lp;
return ((Link *) NULL);
}
/*
* Look for a match in a list of strings. Go through the list, and run
* match() on it. Side effect: if found, this link is moved to the top of
* the list.
*/
Link *find_str_link(Link *lp, char *charptr)
{
if (!charptr)
return ((Link *)NULL);
while(lp!=NULL)
{
if(!match(lp->value.cp, charptr))
return lp;
lp=lp->next;
}
return ((Link *)NULL);
}
Link *make_link()
{
Link *lp;
lp = BlockHeapALLOC(free_Links, Link);
if (lp == (Link *) NULL)
outofmemory();
lp->next = (Link *) NULL; /* just to be paranoid... */
return lp;
}
void free_link(Link *lp)
{
if (BlockHeapFree(free_Links, lp)) {
sendto_ops("list.c couldn't BlockHeapFree(free_Links,lp) lp = %lX",
lp);
sendto_ops("Please report to the bahamut team!");
}
}
DLink *make_dlink()
{
DLink *lp;
lp = BlockHeapALLOC(free_DLinks, DLink);
if (lp == (DLink *) NULL)
outofmemory();
memset(lp, 0, sizeof(*lp));
return lp;
}
DLink *find_dlink(DLink *lp, void *what)
{
while (lp)
{
if (lp->value.cp == what)
return lp;
lp = lp->next;
}
return NULL;
}
void free_dlink(DLink *lp)
{
if (BlockHeapFree(free_DLinks, lp)) {
sendto_ops("list.c couldn't BlockHeapFree(free_DLinks,lp) lp = %lX",
lp);
sendto_ops("Please report to the bahamut team!");
}
}
chanMember *make_chanmember() {
chanMember *mp;
mp = BlockHeapALLOC(free_chanMembers, chanMember);
if (mp == (chanMember *) NULL)
outofmemory();
return mp;
}
void free_chanmember(chanMember *mp)
{
if (BlockHeapFree(free_chanMembers, mp)) {
sendto_ops("list.c couldn't BlockHeapFree(free_chanMembers,mp) "
"mp = %lX", mp);
sendto_ops("Please report to the bahamut team!");
}
}
aClass *make_class()
{
aClass *tmp;
tmp = (aClass *) MyMalloc(sizeof(aClass));
memset((char *) tmp, '\0', sizeof(aClass));
return tmp;
}
aOper *make_oper()
{
aOper *i;
i = (struct Conf_Oper *) MyMalloc(sizeof(aOper));
memset((char *) i, '\0', sizeof(aOper));
return i;
}
aConnect *make_connect()
{
aConnect *i;
i = (struct Conf_Connect *) MyMalloc(sizeof(aConnect));
memset((char *) i, '\0', sizeof(aConnect));
return i;
}
aAllow *make_allow()
{
aAllow *i;
i = (struct Conf_Allow *) MyMalloc(sizeof(aAllow));
memset((char *) i, '\0', sizeof(aAllow));
return i;
}
aPort *make_port()
{
aPort *i;
i = (struct Conf_Port *) MyMalloc(sizeof(aPort));
memset((char *) i, '\0', sizeof(aPort));
return i;
}
Conf_Me *make_me()
{
Conf_Me *i;
i = (struct Conf_Me *) MyMalloc(sizeof(Conf_Me));
memset((char *) i, '\0', sizeof(Conf_Me));
return i;
}
/*
* Attempt to free up some block memory
*
* list_garbage_collect
*
* inputs - NONE output - NONE side effects -
* memory is possibly freed up
*/
void block_garbage_collect()
{
BlockHeapGarbageCollect(free_Links);
BlockHeapGarbageCollect(free_chanMembers);
BlockHeapGarbageCollect(free_local_aClients);
BlockHeapGarbageCollect(free_remote_aClients);
BlockHeapGarbageCollect(free_anUsers);
BlockHeapGarbageCollect(free_channels);
#ifdef FLUD
BlockHeapGarbageCollect(free_fludbots);
#endif
}
u_long
memcount_list(MClist *mc)
{
mc->file = __FILE__;
mc->e_localclients_heap = free_local_aClients;
mc->e_remoteclients_heap = free_remote_aClients;
mc->e_links_heap = free_Links;
mc->e_dlinks_heap = free_DLinks;
mc->e_chanmembers_heap = free_chanMembers;
mc->e_users_heap = free_anUsers;
mc->e_channels_heap = free_channels;
#ifdef FLUD
mc->e_fludbots_heap = free_fludbots;
#endif
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1