/* Routines to maintain a list of online servers.
* by Andrew Kempe (TheShadow)
* E-mail: <theshadow@shadowfire.org>
*
* IRC Services is copyright (c) 1996-2007 Andrew Church.
* E-mail: <achurch@achurch.org>
* Parts written by Andrew Kempe and others.
* This program is free but copyrighted software; see the file COPYING for
* details.
*/
#include "services.h"
#include "modules.h"
#include "hash.h"
/*************************************************************************/
void add_server(Server *server); /* to avoid "no prototype" warning */
void del_server(Server *server); /* same */
DEFINE_HASH(server, Server, name)
static Server *root_server; /* Entry for root server (Services) */
static int16 servercnt = 0; /* Number of online servers */
/* Callback IDs: */
static int cb_create = -1;
static int cb_delete = -1;
/*************************************************************************/
/**************************** Internal Functions *************************/
/*************************************************************************/
/* Allocate a new Server structure, fill in basic values, link it to the
* overall list, and return it. Always successful.
*/
static Server *new_server(const char *servername)
{
Server *server;
servercnt++;
server = scalloc(sizeof(Server), 1);
server->name = sstrdup(servername);
add_server(server);
return server;
}
/* Remove and free a Server structure. */
static void delete_server(Server *server)
{
del_server(server);
servercnt--;
free(server->name);
free(server);
}
/*************************************************************************/
/* Remove the given server. This takes care of recursively removing child
* servers, handling NOQUIT, calling the server delete callback, and
* actually deleting the server data.
*/
static void recursive_squit(Server *parent, const char *reason);
static void squit_server(Server *server, const char *reason)
{
User *user, *nextuser;
recursive_squit(server, reason);
if (protocol_features & PF_NOQUIT) {
#define next snext
#define prev sprev
LIST_FOREACH_SAFE (user, server->userlist, nextuser)
quit_user(user, reason, 0);
#undef next
#undef prev
}
if (!server->fake)
call_callback_2(NULL, cb_delete, server, reason);
delete_server(server);
}
/* "SQUIT" all servers who are linked to us via the specified server by
* deleting them from the server list. The parent server is not deleted,
* so this must be done by the calling function.
*/
static void recursive_squit(Server *parent, const char *reason)
{
Server *server, *nextserver;
server = parent->child;
if (debug >= 2)
log("debug: recursive_squit, parent: %s", parent->name);
while (server) {
nextserver = server->sibling;
if (debug >= 2)
log("debug: recursive_squit, child: %s", server->name);
squit_server(server, reason);
server = nextserver;
}
}
/*************************************************************************/
/**************************** External Functions *************************/
/*************************************************************************/
/* Handle a server SERVER command.
* source = server's hub; !*source indicates this is our hub.
* av[0] = server's name
* If ac < 0, the server in av[0] is assumed to be a "dummy" server that
* should not be passed to the callback (e.g. for JUPE).
*
* When called internally to add a server (from OperServ JUPE, etc.),
* callers may assume that the contents of the argument strings will not be
* modified.
*/
void do_server(const char *source, int ac, char **av)
{
Server *server, *tmpserver;
server = new_server(av[0]);
server->t_join = time(NULL);
server->child = NULL;
server->sibling = NULL;
if (*source) {
server->hub = get_server(source);
if (!server->hub) {
/* PARANOIA: This should NEVER EVER happen, but we check anyway.
*
* I've heard that on older ircds it is possible for "source"
* not to be the new server's hub. This will cause problems.
* -TheShadow
*/
wallops(ServerName,
"WARNING: Could not find server \2%s\2 which is supposed "
"to be the hub for \2%s\2", source, av[0]);
log("server: could not find hub %s for %s", source, av[0]);
}
} else {
server->hub = root_server;
}
if (!server->hub->child) {
server->hub->child = server;
} else {
tmpserver = server->hub->child;
while (tmpserver->sibling)
tmpserver = tmpserver->sibling;
tmpserver->sibling = server;
}
if (ac > 0)
call_callback_1(NULL, cb_create, server);
else
server->fake = 1;
return;
}
/*************************************************************************/
/* Handle a server SQUIT command.
* av[0] = server's name
* av[1] = quit message
*/
void do_squit(const char *source, int ac, char **av)
{
Server *server;
server = get_server(av[0]);
if (server) {
if (server->hub) {
if (server->hub->child == server) {
server->hub->child = server->sibling;
} else {
Server *tmpserver;
for (tmpserver = server->hub->child; tmpserver->sibling;
tmpserver = tmpserver->sibling) {
if (tmpserver->sibling == server) {
tmpserver->sibling = server->sibling;
break;
}
}
}
}
squit_server(server, av[1]);
} else {
wallops(ServerName,
"WARNING: Tried to quit non-existent server: \2%s", av[0]);
log("server: Tried to quit non-existent server: %s", av[0]);
log("server: Input buffer: %s", inbuf);
return;
}
}
/*************************************************************************/
/*************************************************************************/
int server_init(int ac, char **av)
{
Server *server;
cb_create = register_callback(NULL, "server create");
cb_delete = register_callback(NULL, "server delete");
if (cb_create < 0 || cb_delete < 0) {
log("server_init: register_callback() failed\n");
return 0;
}
server = new_server("");
server->fake = 1;
server->t_join = time(NULL);
server->hub = server->child = server->sibling = NULL;
root_server = server;
return 1;
}
/*************************************************************************/
/* Remove all servers; this recursively takes out all users and channels,
* as well.
*/
void server_cleanup(void)
{
uint32 pf = protocol_features;
protocol_features |= PF_NOQUIT;
squit_server(root_server, "server_cleanup");
protocol_features = pf;
unregister_callback(NULL, cb_delete);
unregister_callback(NULL, cb_create);
}
/*************************************************************************/
/* Return information on memory use. Assumes pointers are valid. */
void get_server_stats(long *nservers, long *memuse)
{
Server *server;
long mem;
mem = sizeof(Server) * servercnt;
for (server = first_server(); server; server = next_server())
mem += strlen(server->name)+1;
*nservers = servercnt;
*memuse = mem;
}
/*************************************************************************/
syntax highlighted by Code2HTML, v. 0.9.1