/************************************************************************
* IRC - Internet Relay Chat, ircd/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.
*/
#ifndef lint
static char rcsid[] = "@(#)$Id: list.c,v 1.9 2003/11/28 08:32:41 gvs Exp $";
#endif
#include "os.h"
#include "s_defines.h"
#define LIST_C
#include "s_externs.h"
#undef LIST_C
char *DefInfo = "*Not On This Net*"; /* constant */
#ifdef DEBUGMODE
static struct liststats {
int inuse;
} cloc, crem, users, servs, links, classs, aconfs;
#endif
aServer *svrtop = NULL;
int numclients = 0;
void initlists()
{
#ifdef DEBUGMODE
bzero((char *)&cloc, sizeof(cloc));
bzero((char *)&crem, sizeof(crem));
bzero((char *)&users, sizeof(users));
bzero((char *)&servs, sizeof(servs));
bzero((char *)&links, sizeof(links));
bzero((char *)&classs, sizeof(classs));
bzero((char *)&aconfs, sizeof(aconfs));
#endif
}
void outofmemory()
{
Debug((DEBUG_FATAL, "Out of memory: restarting server..."));
sendto_flag(SCH_NOTICE, "Ouch!!! Out of memory...");
server_reboot("Out of Memory");
}
#ifdef DEBUGMODE
void checklists()
{
aServer *sp;
anUser *up;
for (sp = svrtop; sp; sp = sp->nexts)
if (sp->bcptr->serv != sp)
Debug((DEBUG_ERROR, "svrtop: %#x->%#x->%#x != %#x",
sp, sp->bcptr, sp->bcptr->serv, sp));
}
#endif
/*
** Create a new aClient structure and set it to initial state.
**
** from == NULL, create local client (a client connected
** to a socket).
**
** from, create remote client (behind a socket
** associated with the client defined by
** 'from'). ('from' is a local client!!).
*/
aClient *make_client(from)
aClient *from;
{
Reg aClient *cptr = NULL;
Reg unsigned size;
/*
* Check freelists first to see if we can grab a client without
* having to call malloc.
*/
size = (from == NULL) ? CLIENT_LOCAL_SIZE : CLIENT_REMOTE_SIZE;
if (!(cptr = (aClient *)MyMalloc(size)))
outofmemory();
bzero((char *)cptr, (int)size);
#ifdef DEBUGMODE
if (size == CLIENT_LOCAL_SIZE)
cloc.inuse++;
else
crem.inuse++;
#endif
/* Note: structure is zero (calloc) */
cptr->from = from ? from : cptr; /* 'from' of local client is self! */
cptr->next = NULL; /* For machines with NON-ZERO NULL pointers >;) */
cptr->prev = NULL;
cptr->hnext = NULL;
cptr->user = NULL;
cptr->serv = NULL;
cptr->status = STAT_UNKNOWN;
cptr->fd = -1;
(void)strcpy(cptr->username, "unknown");
cptr->info = DefInfo;
if (size == CLIENT_LOCAL_SIZE)
{
cptr->since = cptr->lasttime = cptr->firsttime = timeofday;
cptr->confs = NULL;
cptr->sockhost[0] = '\0';
cptr->buffer[0] = '\0';
cptr->authfd = -1;
cptr->auth = cptr->username;
cptr->exitc = EXITC_UNDEF;
#ifdef ZIP_LINKS
cptr->zip = NULL;
#endif
}
return (cptr);
}
void free_client(cptr)
aClient *cptr;
{
if (cptr->info != DefInfo)
MyFree(cptr->info);
if (MyConnect(cptr) && cptr->auth != cptr->username)
{
sendto_flag(SCH_ERROR, "Please report to ircd-bug@irc.org about cptr->auth allocated but not free()d!");
istat.is_authmem -= strlen(cptr->auth) + 1;
istat.is_auth -= 1;
MyFree(cptr->auth);
}
MyFree(cptr);
}
/*
** 'make_user' add's an User information block to a client
** if it was not previously allocated.
*/
anUser *make_user(cptr)
aClient *cptr;
{
Reg anUser *user;
user = cptr->user;
if (!user)
{
user = (anUser *)MyMalloc(sizeof(anUser));
#ifdef DEBUGMODE
users.inuse++;
#endif
user->away = NULL;
user->refcnt = 1;
user->joined = 0;
user->flags = 0;
user->channel = NULL;
user->invited = NULL;
user->uwas = NULL;
cptr->user = user;
user->servp = NULL;
user->bcptr = cptr;
if (cptr->next) /* the only cptr->next == NULL is me */
istat.is_users++;
}
return user;
}
aServer *make_server(cptr)
aClient *cptr;
{
Reg aServer *serv = cptr->serv, *sp, *spp = NULL;
if (!serv)
{
serv = (aServer *)MyMalloc(sizeof(aServer));
#ifdef DEBUGMODE
servs.inuse++;
#endif
serv->user = NULL;
serv->snum = -1;
*serv->by = '\0';
*serv->tok = '\0';
serv->stok = 0;
serv->up = NULL;
serv->refcnt = 1;
serv->nexts = NULL;
cptr->serv = serv;
for (sp = svrtop; sp; spp = sp, sp = sp->nexts)
if (spp && ((spp->ltok) + 1 < sp->ltok))
break;
serv->prevs = spp;
if (spp)
{
serv->ltok = spp->ltok + 1;
spp->nexts = serv;
}
else
{ /* Me, myself and I alone */
svrtop = serv;
serv->ltok = 1;
}
if (sp)
{
serv->nexts = sp;
sp->prevs = serv;
}
serv->bcptr = cptr;
SPRINTF(serv->tok, "%d", serv->ltok);
serv->lastload = 0;
}
return cptr->serv;
}
/*
** free_user
** Decrease user reference count by one and realease block,
** if count reaches 0
*/
void free_user(user, cptr)
Reg anUser *user;
aClient *cptr;
{
aServer *serv;
if (--user->refcnt <= 0)
{
if ((serv = user->servp))
{
user->servp = NULL; /* to avoid some impossible loop */
free_server(serv, cptr);
}
if (user->away)
{
istat.is_away--;
istat.is_awaymem -= (strlen(user->away) + 1);
MyFree(user->away);
}
/*
* sanity check
*/
if (user->joined || user->refcnt < 0 ||
user->invited || user->channel || user->uwas ||
user->bcptr)
{
char buf[512];
/*too many arguments for dumpcore() and sendto_flag()*/
SPRINTF(buf, "%#x %#x %#x %#x %d %d %#x (%s)",
user, user->invited, user->channel, user->uwas,
user->joined, user->refcnt,
user->bcptr,
(user->bcptr) ? user->bcptr->name :"none");
#ifdef DEBUGMODE
if (cptr)
dumpcore("%#x user (%s!%s@%s) %s",
cptr, cptr->name, user->username,
#ifdef RUSNET_IRCD
cptr->sockhost
#else
user->host
#endif
, buf);
else
dumpcore("(null) user (<noname>!%s@%s) %s",
user->username, user->host, buf);
#else
if (cptr)
sendto_flag(SCH_ERROR,
"* %#x user (%s!%s@%s) %s *",
cptr, cptr->name, user->username,
#ifdef RUSNET_IRCD
cptr->sockhost
#else
user->host
#endif
, buf);
else
sendto_flag(SCH_ERROR, "* (null) user "
"(<noname>!%s@%s) %s *",
user->username, user->host, buf);
#endif
}
MyFree(user);
#ifdef DEBUGMODE
users.inuse--;
#endif
}
}
void free_server(serv, cptr)
aServer *serv;
aClient *cptr;
{
if (--serv->refcnt <= 0)
{
if (serv->refcnt < 0 || serv->prevs || serv->nexts ||
serv->bcptr || serv->user)
{
char buf[512];
SPRINTF(buf, "%d %#x %#x %#x %#x (%s)",
serv->refcnt, serv->prevs, serv->nexts,
serv->user, serv->bcptr,
(serv->bcptr) ? serv->bcptr->name : "none");
#ifdef DEBUGMODE
dumpcore("%#x server %s %s",
cptr, cptr ? cptr->name : "<noname>", buf);
servs.inuse--;
#else
sendto_flag(SCH_ERROR, "* %#x server %s %s *",
cptr, cptr ? cptr->name : "<noname>", buf);
#endif
}
MyFree(serv);
}
}
/*
* taken the code from ExitOneClient() for this and placed it here.
* - avalon
* remove client **AND** _related structures_ from lists,
* *free* them too. -krys
*/
void remove_client_from_list(cptr)
Reg aClient *cptr;
{
checklist();
if (cptr->hopcount == 0) /* is there another way, at this point? */
istat.is_localc--;
else
istat.is_remc--;
if (cptr->prev)
cptr->prev->next = cptr->next;
else
{
client = cptr->next;
client->prev = NULL;
}
if (cptr->next)
cptr->next->prev = cptr->prev;
if (cptr->user)
{
istat.is_users--;
/* decrement reference counter, and eventually free it */
cptr->user->bcptr = NULL;
(void)free_user(cptr->user, cptr);
}
if (cptr->serv)
{
/* has to be removed from the list of aServer structures */
if (cptr->serv->nexts)
cptr->serv->nexts->prevs = cptr->serv->prevs;
if (cptr->serv->prevs)
cptr->serv->prevs->nexts = cptr->serv->nexts;
if (svrtop == cptr->serv)
svrtop = cptr->serv->nexts;
cptr->serv->prevs = NULL;
cptr->serv->nexts = NULL;
if (cptr->serv->user)
{
free_user(cptr->serv->user, cptr);
cptr->serv->user = NULL;
}
/* decrement reference counter, and eventually free it */
cptr->serv->bcptr = NULL;
free_server(cptr->serv, cptr);
}
if (cptr->service)
/*
** has to be removed from the list of aService structures,
** no reference counter for services, thus this part of the
** code can safely be included in free_service()
*/
free_service(cptr);
#ifdef DEBUGMODE
if (cptr->fd == -2)
cloc.inuse--;
else
crem.inuse--;
#endif
(void)free_client(cptr);
numclients--;
return;
}
/*
* move the client aClient struct before its server's
*/
void reorder_client_in_list(cptr)
aClient *cptr;
{
if (cptr->user == NULL && cptr->service == NULL)
return;
/* update neighbours */
if (cptr->next)
cptr->next->prev = cptr->prev;
if (cptr->prev)
cptr->prev->next = cptr->next;
else
client = cptr->next;
/* re-insert */
if (cptr->user)
{
cptr->next = cptr->user->servp->bcptr;
cptr->prev = cptr->user->servp->bcptr->prev;
#ifdef DEBUGMODE
sendto_flag(SCH_DEBUG, "%p [%s] moved before server: %p [%s]",
cptr, cptr->name, cptr->user->servp->bcptr,
cptr->user->servp->bcptr->name);
#endif
}
else if (cptr->service)
{
cptr->next = cptr->service->servp->bcptr;
cptr->prev = cptr->service->servp->bcptr->prev;
}
/* update new neighbours */
if (cptr->prev)
cptr->prev->next = cptr;
else
client = cptr;
cptr->next->prev = cptr;
}
/*
* 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(cptr)
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.
*/
if (cptr->from == cptr)
istat.is_localc++;
else
istat.is_remc++;
if (cptr->user)
istat.is_users++;
cptr->next = client;
client = cptr;
if (cptr->next)
cptr->next->prev = cptr;
numclients++;
return;
}
/*
* Look for ptr in the linked listed pointed to by link.
*/
Link *find_user_link(lp, ptr)
Reg Link *lp;
Reg aClient *ptr;
{
if (ptr)
for (; lp; lp = lp->next)
if (lp->value.cptr == ptr)
return (lp);
return NULL;
}
Link *find_channel_link(lp, ptr)
Reg Link *lp;
Reg aChannel *ptr;
{
if (ptr)
for (; lp; lp = lp->next)
if (lp->value.chptr == ptr)
return (lp);
return NULL;
}
Link *make_link()
{
Reg Link *lp;
lp = (Link *)MyMalloc(sizeof(Link));
#ifdef DEBUGMODE
links.inuse++;
#endif
lp->flags = 0;
return lp;
}
void free_link(lp)
Reg Link *lp;
{
MyFree(lp);
#ifdef DEBUGMODE
links.inuse--;
#endif
}
aClass *make_class()
{
Reg aClass *tmp;
tmp = (aClass *)MyMalloc(sizeof(aClass));
#ifdef DEBUGMODE
classs.inuse++;
#endif
return tmp;
}
void free_class(tmp)
Reg aClass *tmp;
{
MyFree(tmp);
#ifdef DEBUGMODE
classs.inuse--;
#endif
}
aConfItem *make_conf()
{
Reg aConfItem *aconf;
aconf = (struct ConfItem *)MyMalloc(sizeof(aConfItem));
#ifdef DEBUGMODE
aconfs.inuse++;
#endif
istat.is_conf++;
istat.is_confmem += sizeof(aConfItem);
bzero((char *)&aconf->ipnum, sizeof(struct in_addr));
aconf->clients = aconf->port = 0;
aconf->next = NULL;
aconf->host = aconf->passwd = aconf->name = NULL;
aconf->ping = NULL;
aconf->status = CONF_ILLEGAL;
aconf->pref = -1;
aconf->hold = time(NULL);
Class(aconf) = NULL;
return (aconf);
}
void delist_conf(aconf)
aConfItem *aconf;
{
if (aconf == conf)
conf = conf->next;
else
{
aConfItem *bconf;
for (bconf = conf; aconf != bconf->next; bconf = bconf->next)
;
bconf->next = aconf->next;
}
aconf->next = NULL;
}
void free_conf(aconf)
aConfItem *aconf;
{
del_queries((char *)aconf);
istat.is_conf--;
istat.is_confmem -= aconf->host ? strlen(aconf->host)+1 : 0;
istat.is_confmem -= aconf->passwd ? strlen(aconf->passwd)+1 : 0;
istat.is_confmem -= aconf->name ? strlen(aconf->name)+1 : 0;
istat.is_confmem -= aconf->ping ? sizeof(*aconf->ping) : 0;
istat.is_confmem -= sizeof(aConfItem);
MyFree(aconf->host);
if (aconf->passwd)
bzero(aconf->passwd, strlen(aconf->passwd));
if (aconf->ping)
MyFree(aconf->ping);
MyFree(aconf->passwd);
MyFree(aconf->name);
MyFree(aconf);
#ifdef DEBUGMODE
aconfs.inuse--;
#endif
return;
}
#ifdef DEBUGMODE
void send_listinfo(cptr, name)
aClient *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);
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(anUser));
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(aServer));
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(Link));
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(aClass));
mem += tmp;
inuse += classs.inuse,
sendto_one(cptr, ":%s %d %s :Confs: inuse: %d(%d)",
me.name, RPL_STATSDEBUG, name, aconfs.inuse,
tmp = aconfs.inuse * sizeof(aConfItem));
mem += tmp;
inuse += aconfs.inuse,
sendto_one(cptr, ":%s %d %s :Totals: inuse %d %d",
me.name, RPL_STATSDEBUG, name, inuse, mem);
}
#endif
void add_fd(fd, ary)
int fd;
FdAry *ary;
{
Debug((DEBUG_DEBUG,"add_fd(%d,%#x)", fd, ary));
if (fd >= 0)
ary->fd[++(ary->highest)] = fd;
}
int del_fd(fd, ary)
int fd;
FdAry *ary;
{
int i;
Debug((DEBUG_DEBUG,"del_fd(%d,%#x)", fd, ary));
if ((ary->highest == -1) || (fd < 0))
return -1;
for (i = 0; i <= ary->highest; i++)
if (ary->fd[i] == fd)
break;
if (i < ary->highest)
{
ary->fd[i] = ary->fd[ary->highest--];
return 0;
}
else if (i > ary->highest)
return -1;
ary->highest--;
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1